walters / rpms / gdm

Forked from rpms/gdm 6 years ago
Clone
Blob Blame History Raw
From 1324842ceb10360ca3c7362dbf0f86f12ec0dd7f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 16 Jan 2009 11:00:08 -0500
Subject: [PATCH 01/47] Introduce new Conversation object

We want to eventually support having multiple
simultaneous PAM conversations in one login
screen (so, e.g., username/password, smart card, and
fingerprint all work at the same time).

This commit refactors the session code to be in terms
of a conversation object.  With this change, it should
be easier later to have multiple conversation objects.

The conversation is named by the pam service the login
screen is talking to.
---
 daemon/gdm-factory-slave.c      |    5 +-
 daemon/gdm-product-slave.c      |   29 +++-
 daemon/gdm-session-direct.c     |  318 ++++++++++++++++++++++++---------------
 daemon/gdm-session-private.h    |    3 +-
 daemon/gdm-session-relay.c      |   15 ++-
 daemon/gdm-session-worker-job.c |    7 +
 daemon/gdm-session-worker-job.h |    2 +
 daemon/gdm-session.c            |   12 +-
 daemon/gdm-session.h            |    9 +-
 daemon/gdm-simple-slave.c       |    3 -
 daemon/test-session.c           |    5 +-
 11 files changed, 263 insertions(+), 145 deletions(-)

diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c
index 606c995..bd98445 100644
--- a/daemon/gdm-factory-slave.c
+++ b/daemon/gdm-factory-slave.c
@@ -181,7 +181,8 @@ on_session_secret_info_query (GdmSession      *session,
 
 static void
 on_session_conversation_started (GdmSession      *session,
-                                 GdmFactorySlave *slave)
+                                 GdmFactorySlave *slave,
+                                 const char      *service_name)
 {
         g_debug ("GdmFactorySlave: session conversation started");
 
@@ -388,7 +389,7 @@ on_session_relay_connected (GdmSessionRelay *session,
 {
         g_debug ("GdmFactorySlave: Relay Connected");
 
-        gdm_session_start_conversation (GDM_SESSION (slave->priv->session));
+        gdm_session_start_conversation (GDM_SESSION (slave->priv->session), "gdm");
 }
 
 static void
diff --git a/daemon/gdm-product-slave.c b/daemon/gdm-product-slave.c
index 1fff780..ec6300d 100644
--- a/daemon/gdm-product-slave.c
+++ b/daemon/gdm-product-slave.c
@@ -246,19 +246,21 @@ relay_session_started (GdmProductSlave *slave,
 }
 
 static void
-relay_session_conversation_started (GdmProductSlave *slave)
+relay_session_conversation_started (GdmProductSlave *slave,
+                                    const char      *service_name)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "ConversationStarted");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "ConversationStarted", service_name);
 }
 
 static void
 on_session_conversation_started (GdmSession      *session,
+                                 const char      *service_name,
                                  GdmProductSlave *slave)
 {
         g_debug ("GdmProductSlave: session conversation started");
 
-        relay_session_conversation_started (slave);
+        relay_session_conversation_started (slave, service_name);
 }
 
 static void
@@ -805,7 +807,24 @@ static void
 on_relay_start_conversation (GdmProductSlave *slave,
                              DBusMessage     *message)
 {
-        gdm_session_start_conversation (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
diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 77d5801..dff3e68 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -66,6 +66,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 */
@@ -78,8 +88,7 @@ struct _GdmSessionDirectPrivate
         char                *selected_user;
         char                *user_x11_authority_file;
 
-        DBusMessage         *message_pending_reply;
-        DBusConnection      *worker_connection;
+        GdmSessionConversation *conversation;
 
         GdmSessionWorkerJob *job;
         GPid                 session_pid;
@@ -120,39 +129,39 @@ G_DEFINE_TYPE_WITH_CODE (GdmSessionDirect,
                                                 gdm_session_iface_init))
 
 static gboolean
-send_dbus_message (DBusConnection *connection,
-                   DBusMessage    *message)
+send_dbus_message (GdmSessionConversation *conversation,
+                   DBusMessage            *message)
 {
         gboolean is_connected;
         gboolean sent;
 
         g_return_val_if_fail (message != NULL, FALSE);
 
-        if (connection == NULL) {
+        if (conversation->worker_connection == NULL) {
                 g_warning ("There is no valid connection");
                 return FALSE;
         }
 
-        is_connected = dbus_connection_get_is_connected (connection);
+        is_connected = dbus_connection_get_is_connected (conversation->worker_connection);
         if (! is_connected) {
                 g_warning ("Not connected!");
                 return FALSE;
         }
 
-        sent = dbus_connection_send (connection, message, NULL);
+        sent = dbus_connection_send (conversation->worker_connection, message, NULL);
 
         return sent;
 }
 
 static void
-send_dbus_string_signal (GdmSessionDirect *session,
+send_dbus_string_signal (GdmSessionConversation *conversation,
                          const char *name,
                          const char *text)
 {
         DBusMessage    *message;
         DBusMessageIter iter;
 
-        g_return_if_fail (session != NULL);
+        g_return_if_fail (conversation != NULL);
 
         message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
                                            GDM_SESSION_DBUS_INTERFACE,
@@ -161,7 +170,7 @@ send_dbus_string_signal (GdmSessionDirect *session,
         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)");
         }
@@ -170,18 +179,18 @@ send_dbus_string_signal (GdmSessionDirect *session,
 }
 
 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);
         }
 
@@ -193,22 +202,32 @@ on_authentication_failed (GdmSession *session,
                           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 = impl->priv->conversation;
+        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)
 {
         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 = impl->priv->conversation;
+        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
@@ -216,11 +235,16 @@ on_session_start_failed (GdmSession *session,
                          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 = impl->priv->conversation;
+        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
@@ -228,6 +252,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,
@@ -770,54 +795,52 @@ gdm_session_direct_handle_username_changed (GdmSessionDirect *session,
 }
 
 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       *answer)
+answer_pending_query (GdmSessionConversation *conversation,
+                      const char             *answer)
 {
         DBusMessage    *reply;
         DBusMessageIter iter;
 
-        g_assert (session->priv->message_pending_reply != NULL);
-
-        reply = dbus_message_new_method_return (session->priv->message_pending_reply);
+        reply = dbus_message_new_method_return (conversation->message_pending_reply);
         dbus_message_iter_init_append (reply, &iter);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &answer);
 
-        dbus_connection_send (session->priv->worker_connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        dbus_message_unref (session->priv->message_pending_reply);
-        session->priv->message_pending_reply = NULL;
+        dbus_message_unref (conversation->message_pending_reply);
+        conversation->message_pending_reply = NULL;
 }
 
 static void
-set_pending_query (GdmSessionDirect *session,
-                   DBusMessage      *message)
+set_pending_query (GdmSessionConversation *conversation,
+                   DBusMessage            *message)
 {
-        g_assert (session->priv->message_pending_reply == NULL);
+        g_assert (conversation->message_pending_reply == NULL);
 
-        session->priv->message_pending_reply = dbus_message_ref (message);
+        conversation->message_pending_reply = dbus_message_ref (message);
 }
 
 static DBusHandlerResult
@@ -827,6 +850,9 @@ gdm_session_direct_handle_info_query (GdmSessionDirect *session,
 {
         DBusError    error;
         const char  *text;
+        GdmSessionConversation *conversation;
+
+        conversation = session->priv->conversation;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
@@ -835,7 +861,7 @@ gdm_session_direct_handle_info_query (GdmSessionDirect *session,
                 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);
@@ -850,6 +876,9 @@ gdm_session_direct_handle_secret_info_query (GdmSessionDirect *session,
 {
         DBusError    error;
         const char  *text;
+        GdmSessionConversation *conversation;
+
+        conversation = session->priv->conversation;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
@@ -858,7 +887,7 @@ gdm_session_direct_handle_secret_info_query (GdmSessionDirect *session,
                 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);
@@ -898,9 +927,13 @@ gdm_session_direct_handle_cancel_pending_query (GdmSessionDirect *session,
                                                 DBusMessage      *message)
 {
         DBusMessage *reply;
+        GdmSessionConversation *conversation;
 
         g_debug ("GdmSessionDirect: worker cancelling pending query");
-        cancel_pending_query (session);
+
+        conversation = session->priv->conversation;
+
+        cancel_pending_query (conversation);
 
         reply = dbus_message_new_method_return (message);
         dbus_connection_send (connection, reply, NULL);
@@ -1522,16 +1555,18 @@ handle_connection (DBusServer      *server,
                    void            *user_data)
 {
         GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
+        GdmSessionConversation *conversation;
 
         g_debug ("GdmSessionDirect: Handing new connection");
 
-        if (session->priv->worker_connection == NULL) {
+        conversation = session->priv->conversation;
+        if (conversation->worker_connection == NULL) {
                 DBusObjectPathVTable vtable = { &session_unregister_handler,
                                                 &session_message_handler,
                                                 NULL, NULL, NULL, NULL
                 };
 
-                session->priv->worker_connection = new_connection;
+                conversation->worker_connection = new_connection;
                 dbus_connection_ref (new_connection);
                 dbus_connection_setup_with_g_main (new_connection, NULL);
 
@@ -1549,7 +1584,8 @@ handle_connection (DBusServer      *server,
                                                       session);
 
                 g_debug ("GdmSessionDirect: Emitting conversation-started signal");
-                _gdm_session_conversation_started (GDM_SESSION (session));
+                _gdm_session_conversation_started (GDM_SESSION (session),
+                                                   conversation->service_name);
         }
 }
 
@@ -1619,8 +1655,6 @@ gdm_session_direct_init (GdmSessionDirect *session)
                           G_CALLBACK (on_session_exited),
                           NULL);
 
-        session->priv->session_pid = -1;
-
         session->priv->environment = g_hash_table_new_full (g_str_hash,
                                                             g_str_equal,
                                                             (GDestroyNotify) g_free,
@@ -1632,7 +1666,7 @@ gdm_session_direct_init (GdmSessionDirect *session)
 
 static void
 worker_started (GdmSessionWorkerJob *job,
-                GdmSessionDirect    *session)
+                GdmSessionConversation *conversation)
 {
         g_debug ("GdmSessionDirect: Worker job started");
 }
@@ -1640,87 +1674,105 @@ 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_running) {
-                _gdm_session_session_exited (GDM_SESSION (session), code);
+        if (conversation->session->priv->is_running) {
+                _gdm_session_session_exited (GDM_SESSION (conversation->session), code);
         }
 }
 
 static void
 worker_died (GdmSessionWorkerJob *job,
              int                  signum,
-             GdmSessionDirect    *session)
+             GdmSessionConversation *conversation)
 {
         g_debug ("GdmSessionDirect: Worker job died: %d", signum);
 
-        if (session->priv->is_running) {
-                _gdm_session_session_died (GDM_SESSION (session), signum);
+        if (conversation->session->priv->is_running) {
+                _gdm_session_session_died (GDM_SESSION (conversation->session), signum);
         }
 }
 
-static gboolean
-start_worker (GdmSessionDirect *session)
+static GdmSessionConversation *
+start_conversation (GdmSessionDirect *session,
+                    const char       *service_name)
 {
-        gboolean res;
+        GdmSessionConversation *conversation;
 
-        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,
+        conversation = g_new0 (GdmSessionConversation, 1);
+        conversation->session = session;
+        conversation->service_name = g_strdup (service_name);
+        conversation->worker_pid = -1;
+        conversation->job = gdm_session_worker_job_new ();
+        gdm_session_worker_job_set_server_address (conversation->job, session->priv->server_address);
+        g_signal_connect (conversation->job,
                           "started",
                           G_CALLBACK (worker_started),
-                          session);
-        g_signal_connect (session->priv->job,
+                          conversation);
+        g_signal_connect (conversation->job,
                           "exited",
                           G_CALLBACK (worker_exited),
-                          session);
-        g_signal_connect (session->priv->job,
+                          conversation);
+        g_signal_connect (conversation->job,
                           "died",
                           G_CALLBACK (worker_died),
-                          session);
+                          conversation);
 
-        res = gdm_session_worker_job_start (session->priv->job);
+        if (!gdm_session_worker_job_start (conversation->job)) {
+                g_object_unref (conversation->job);
+                g_free (conversation->service_name);
+                g_free (conversation);
+                return NULL;
+        }
 
-        return res;
+        conversation->worker_pid = gdm_session_worker_job_get_pid (conversation->job);
+
+        return conversation;
 }
 
 static void
-stop_worker (GdmSessionDirect *session)
+stop_conversation (GdmSessionConversation *conversation)
 {
-        g_signal_handlers_disconnect_by_func (session->priv->job,
+        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);
+        cancel_pending_query (conversation);
 
-        if (session->priv->worker_connection != NULL) {
-                dbus_connection_close (session->priv->worker_connection);
-                session->priv->worker_connection = NULL;
+        if (conversation->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);
+        g_free (conversation->service_name);
+        g_free (conversation);
 }
 
 static void
-gdm_session_direct_start_conversation (GdmSession *session)
+gdm_session_direct_start_conversation (GdmSession *session,
+                                       const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
 
         g_return_if_fail (session != NULL);
 
-        g_debug ("GdmSessionDirect: Starting conversation");
+        g_debug ("GdmSessionDirect: starting conversation");
 
-        start_worker (impl);
+        impl->priv->conversation = start_conversation (impl, service_name);
 }
 
 static void
@@ -1733,6 +1785,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);
 
@@ -1770,7 +1823,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 = session->priv->conversation;
+        if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "Setup");
         }
 
@@ -1788,6 +1842,7 @@ send_setup_for_user (GdmSessionDirect *session,
         const char     *display_hostname;
         const char     *display_x11_authority_file;
         const char     *selected_user;
+        GdmSessionConversation *conversation;
 
         g_assert (service_name != NULL);
 
@@ -1831,7 +1886,8 @@ send_setup_for_user (GdmSessionDirect *session,
         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 = session->priv->conversation;
+        if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "SetupForUser");
         }
 
@@ -1845,7 +1901,8 @@ gdm_session_direct_setup (GdmSession *session,
         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 (impl->priv->conversation != NULL);
+        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
         send_setup (impl, service_name);
         gdm_session_direct_defaults_changed (impl);
@@ -1859,7 +1916,8 @@ gdm_session_direct_setup_for_user (GdmSession *session,
         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 (impl->priv->conversation != NULL);
+        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
         g_return_if_fail (username != NULL);
 
         gdm_session_direct_select_user (session, username);
@@ -1872,22 +1930,28 @@ static void
 gdm_session_direct_authenticate (GdmSession *session)
 {
         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));
+        g_return_if_fail (impl->priv->conversation != NULL);
+        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
-        send_dbus_void_signal (impl, "Authenticate");
+        conversation = impl->priv->conversation;
+        send_dbus_void_signal (conversation, "Authenticate");
 }
 
 static void
 gdm_session_direct_authorize (GdmSession *session)
 {
         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));
+        g_return_if_fail (impl->priv->conversation != NULL);
+        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
-        send_dbus_void_signal (impl, "Authorize");
+        conversation = impl->priv->conversation;
+        send_dbus_void_signal (conversation, "Authorize");
 }
 
 static void
@@ -1895,16 +1959,19 @@ gdm_session_direct_accredit (GdmSession *session,
                              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));
+        g_return_if_fail (impl->priv->conversation != NULL);
+        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
+        conversation = impl->priv->conversation;
         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 ();
@@ -1918,6 +1985,7 @@ send_environment_variable (const char       *key,
 {
         DBusMessage    *message;
         DBusMessageIter iter;
+        GdmSessionConversation *conversation;
 
         message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
                                            GDM_SESSION_DBUS_INTERFACE,
@@ -1927,7 +1995,8 @@ send_environment_variable (const char       *key,
         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)) {
+        conversation = session->priv->conversation;
+        if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "SetEnvironmentVariable");
         }
 
@@ -2070,6 +2139,7 @@ static void
 gdm_session_direct_start_session (GdmSession *session)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
         char             *command;
         char             *program;
 
@@ -2089,7 +2159,8 @@ gdm_session_direct_start_session (GdmSession *session)
         setup_session_environment (impl);
         send_environment (impl);
 
-        send_dbus_string_signal (impl, "StartProgram", program);
+        conversation = impl->priv->conversation;
+        send_dbus_string_signal (conversation, "StartProgram", program);
         g_free (program);
 }
 
@@ -2102,16 +2173,12 @@ gdm_session_direct_close (GdmSession *session)
 
         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);
         }
 
         g_free (impl->priv->selected_user);
@@ -2149,10 +2216,13 @@ gdm_session_direct_answer_query  (GdmSession *session,
                                   const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_return_if_fail (session != NULL);
 
-        answer_pending_query (impl, text);
+        conversation = impl->priv->conversation;
+
+        answer_pending_query (conversation, text);
 }
 
 static void
@@ -2162,7 +2232,7 @@ gdm_session_direct_cancel  (GdmSession *session)
 
         g_return_if_fail (session != NULL);
 
-        cancel_pending_query (impl);
+        cancel_pending_query (impl->priv->conversation);
 }
 
 char *
@@ -2227,6 +2297,7 @@ gdm_session_direct_select_session (GdmSession *session,
                                    const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_free (impl->priv->selected_session);
 
@@ -2236,7 +2307,8 @@ gdm_session_direct_select_session (GdmSession *session,
                 impl->priv->selected_session = g_strdup (text);
         }
 
-        send_dbus_string_signal (impl, "SetSessionName",
+        conversation = impl->priv->conversation;
+        send_dbus_string_signal (conversation, "SetSessionName",
                                  get_session_name (impl));
 }
 
@@ -2245,6 +2317,7 @@ gdm_session_direct_select_language (GdmSession *session,
                                     const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_free (impl->priv->selected_language);
 
@@ -2254,7 +2327,8 @@ gdm_session_direct_select_language (GdmSession *session,
                 impl->priv->selected_language = g_strdup (text);
         }
 
-        send_dbus_string_signal (impl, "SetLanguageName",
+        conversation = impl->priv->conversation;
+        send_dbus_string_signal (conversation, "SetLanguageName",
                                  get_language_name (impl));
 }
 
@@ -2263,6 +2337,7 @@ gdm_session_direct_select_layout (GdmSession *session,
                                   const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_free (impl->priv->selected_layout);
 
@@ -2272,7 +2347,8 @@ gdm_session_direct_select_layout (GdmSession *session,
                 impl->priv->selected_layout = g_strdup (text);
         }
 
-        send_dbus_string_signal (impl, "SetLayoutName",
+        conversation = impl->priv->conversation;
+        send_dbus_string_signal (conversation, "SetLayoutName",
                                  get_layout_name (impl));
 }
 
diff --git a/daemon/gdm-session-private.h b/daemon/gdm-session-private.h
index 7ccf0d7..74b6069 100644
--- a/daemon/gdm-session-private.h
+++ b/daemon/gdm-session-private.h
@@ -27,7 +27,8 @@
 G_BEGIN_DECLS
 
 /* state changes */
-void             _gdm_session_conversation_started         (GdmSession   *session);
+void             _gdm_session_conversation_started         (GdmSession   *session,
+                                                            const char   *service_name);
 void             _gdm_session_setup_complete               (GdmSession   *session);
 void             _gdm_session_setup_failed                 (GdmSession   *session,
                                                             const char   *message);
diff --git a/daemon/gdm-session-relay.c b/daemon/gdm-session-relay.c
index da0ecb3..a0b4cbf 100644
--- a/daemon/gdm-session-relay.c
+++ b/daemon/gdm-session-relay.c
@@ -180,10 +180,11 @@ send_dbus_void_signal (GdmSessionRelay *session_relay,
 }
 
 static void
-gdm_session_relay_start_conversation (GdmSession *session)
+gdm_session_relay_start_conversation (GdmSession *session,
+                                      const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "StartConversation");
+        send_dbus_string_signal (impl, "StartConversation", service_name);
 }
 
 static void
@@ -728,8 +729,14 @@ handle_conversation_started (GdmSessionRelay *session_relay,
 {
         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: Conversation Started");
 
@@ -737,7 +744,7 @@ handle_conversation_started (GdmSessionRelay *session_relay,
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_conversation_started (GDM_SESSION (session_relay));
+        _gdm_session_conversation_started (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -812,6 +819,7 @@ do_introspect (DBusConnection *connection,
         xml = g_string_append (xml,
                                "  <interface name=\"org.gnome.DisplayManager.SessionRelay\">\n"
                                "    <method name=\"ConversationStarted\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"SetupComplete\">\n"
                                "    </method>\n"
@@ -873,6 +881,7 @@ do_introspect (DBusConnection *connection,
                                "    </signal>\n"
 
                                "    <signal name=\"StartConversation\">\n"
+                               "      <arg name=\"service_name\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Close\">\n"
                                "    </signal>\n"
diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c
index 633d6e2..9c020fa 100644
--- a/daemon/gdm-session-worker-job.c
+++ b/daemon/gdm-session-worker-job.c
@@ -296,6 +296,13 @@ gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job)
         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)
diff --git a/daemon/gdm-session-worker-job.h b/daemon/gdm-session-worker-job.h
index 5ad1c92..d24f025 100644
--- a/daemon/gdm-session-worker-job.h
+++ b/daemon/gdm-session-worker-job.h
@@ -60,6 +60,8 @@ void                    gdm_session_worker_job_set_server_address (GdmSessionWor
 gboolean                gdm_session_worker_job_start              (GdmSessionWorkerJob *session_worker_job);
 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 --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 1e2af92..1820e29 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -80,11 +80,12 @@ gdm_session_get_type (void)
 }
 
 void
-gdm_session_start_conversation (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)->start_conversation (session);
+        GDM_SESSION_GET_IFACE (session)->start_conversation (session, service_name);
 }
 
 void
@@ -220,7 +221,7 @@ gdm_session_class_init (gpointer g_iface)
                               G_STRUCT_OFFSET (GdmSessionIface, conversation_started),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
                               0);
         signals [SETUP_COMPLETE] =
@@ -654,10 +655,11 @@ _gdm_session_session_died (GdmSession   *session,
 }
 
 void
-_gdm_session_conversation_started (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);
+        g_signal_emit (session, signals [CONVERSATION_STARTED], 0, service_name);
 }
 
 void
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index fb199d3..00c2fa4 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -45,7 +45,8 @@ struct _GdmSessionIface
         GTypeInterface base_iface;
 
         /* Methods */
-        void (* start_conversation)          (GdmSession   *session);
+        void (* start_conversation)          (GdmSession   *session,
+                                              const char   *service_name);
         void (* setup)                       (GdmSession   *session,
                                               const char   *service_name);
         void (* setup_for_user)              (GdmSession   *session,
@@ -107,7 +108,8 @@ struct _GdmSessionIface
                                               int           exit_code);
         void (* session_died)                (GdmSession   *session,
                                               int           signal_number);
-        void (* conversation_started)        (GdmSession   *session);
+        void (* conversation_started)        (GdmSession   *session,
+                                              const char   *service_name);
         void (* closed)                      (GdmSession   *session);
         void (* selected_user_changed)       (GdmSession   *session,
                                               const char   *text);
@@ -122,7 +124,8 @@ struct _GdmSessionIface
 
 GType    gdm_session_get_type                    (void) G_GNUC_CONST;
 
-void     gdm_session_start_conversation          (GdmSession *session);
+void     gdm_session_start_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,
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 59e3530..08f88b7 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -179,7 +179,6 @@ reset_session (GdmSimpleSlave *slave)
 {
         destroy_session (slave);
         create_new_session (slave);
-        gdm_session_start_conversation (GDM_SESSION (slave->priv->session));
 }
 
 static gboolean
@@ -862,8 +861,6 @@ on_greeter_connected (GdmGreeterServer *greeter_server,
 
         g_debug ("GdmSimpleSlave: Greeter connected");
 
-        gdm_session_start_conversation (GDM_SESSION (slave->priv->session));
-
         g_object_get (slave,
                       "display-is-local", &display_is_local,
                       NULL);
diff --git a/daemon/test-session.c b/daemon/test-session.c
index 9a4d7cf..ed13ff7 100644
--- a/daemon/test-session.c
+++ b/daemon/test-session.c
@@ -34,11 +34,12 @@ static GMainLoop *loop;
 
 static void
 on_conversation_started (GdmSession *session,
+                         const char *service_name,
                          const char *username)
 {
         g_debug ("Got conversation started: calling setup...");
 
-        gdm_session_setup (session, "gdm");
+        gdm_session_setup (session, service_name);
 }
 
 static void
@@ -256,7 +257,7 @@ main (int   argc,
                         username = argv[1];
                 }
 
-                gdm_session_start_conversation (GDM_SESSION (session));
+                gdm_session_start_conversation (GDM_SESSION (session), "gdm");
 
                 g_signal_connect (session,
                                   "conversation-started",
-- 
1.7.0.1


From 2ea9e937438d49f6287e2ea00f016ff692c4fc16 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 4 Feb 2009 10:55:03 -0500
Subject: [PATCH 02/47] Rename session worker to the service it's managing

This way when we're running multiple PAM conversations at once
it will be obvious which worker is managing which conversation.
---
 daemon/gdm-session-direct.c     |    7 ++++-
 daemon/gdm-session-worker-job.c |   63 ++++++++++++++++++++++++++++++--------
 daemon/gdm-session-worker-job.h |    3 +-
 3 files changed, 57 insertions(+), 16 deletions(-)

diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index dff3e68..abc3c9d 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -1700,6 +1700,7 @@ start_conversation (GdmSessionDirect *session,
                     const char       *service_name)
 {
         GdmSessionConversation *conversation;
+        char                   *job_name;
 
         conversation = g_new0 (GdmSessionConversation, 1);
         conversation->session = session;
@@ -1720,12 +1721,16 @@ start_conversation (GdmSessionDirect *session,
                           G_CALLBACK (worker_died),
                           conversation);
 
-        if (!gdm_session_worker_job_start (conversation->job)) {
+        job_name = g_strdup_printf ("pam: %s", service_name);
+        if (!gdm_session_worker_job_start (conversation->job,
+                                           job_name)) {
                 g_object_unref (conversation->job);
                 g_free (conversation->service_name);
                 g_free (conversation);
+                g_free (job_name);
                 return NULL;
         }
+        g_free (job_name);
 
         conversation->worker_pid = gdm_session_worker_job_get_pid (conversation->job);
 
diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c
index 9c020fa..0327d77 100644
--- a/daemon/gdm-session-worker-job.c
+++ b/daemon/gdm-session-worker-job.c
@@ -149,6 +149,37 @@ copy_environment_to_hash (GHashTable *hash)
 }
 
 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;
@@ -171,31 +202,31 @@ get_job_environment (GdmSessionWorkerJob *job)
 }
 
 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,
@@ -204,6 +235,9 @@ gdm_session_worker_job_spawn (GdmSessionWorkerJob *session_worker_job)
                                         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);
 
@@ -220,7 +254,6 @@ gdm_session_worker_job_spawn (GdmSessionWorkerJob *session_worker_job)
                                                                       (GChildWatchFunc)session_worker_job_child_watch,
                                                                       session_worker_job);
 
-        g_strfreev (argv);
  out:
 
         return ret;
@@ -233,13 +266,14 @@ gdm_session_worker_job_spawn (GdmSessionWorkerJob *session_worker_job)
  * 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,6 +321,7 @@ gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job)
         g_debug ("GdmSessionWorkerJob: Stopping job pid:%d", session_worker_job->priv->pid);
 
         res = gdm_signal_pid (session_worker_job->priv->pid, SIGTERM);
+
         if (res < 0) {
                 g_warning ("Unable to kill session worker process");
         } else {
diff --git a/daemon/gdm-session-worker-job.h b/daemon/gdm-session-worker-job.h
index d24f025..4833f23 100644
--- a/daemon/gdm-session-worker-job.h
+++ b/daemon/gdm-session-worker-job.h
@@ -57,7 +57,8 @@ GType                   gdm_session_worker_job_get_type           (void);
 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);
-- 
1.7.0.1


From 3bfd8954024306167813532c0f641f19b1fbde18 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 16 Jan 2009 13:01:48 -0500
Subject: [PATCH 03/47] Make greeter explicitly request PAM conversation

Now the greeter has to say what PAM stack it wants the slave to
run.  When that stack is ready, we emit the Ready signal as
before, but now the Ready signal carries a string argument
saying which service is ready to converse.

When we support multiple PAM stacks, the greeter will call
StartConversation for each stack, and will keep the UI
associated with each stack disabled until the Ready signals
come back one-by-one.
---
 daemon/gdm-factory-slave.c               |    3 +-
 daemon/gdm-greeter-server.c              |   53 ++++++++++++++++++++++++++++--
 daemon/gdm-greeter-server.h              |    5 ++-
 daemon/gdm-simple-slave.c                |   22 +++++++++++-
 gui/simple-greeter/gdm-greeter-client.c  |   18 ++++++----
 gui/simple-greeter/gdm-greeter-client.h  |    4 ++-
 gui/simple-greeter/gdm-greeter-session.c |    4 ++
 7 files changed, 94 insertions(+), 15 deletions(-)

diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c
index bd98445..ca3ba48 100644
--- a/daemon/gdm-factory-slave.c
+++ b/daemon/gdm-factory-slave.c
@@ -186,7 +186,8 @@ on_session_conversation_started (GdmSession      *session,
 {
         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
diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index cecce83..ffca7ec 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -69,6 +69,7 @@ enum {
 };
 
 enum {
+        START_CONVERSATION,
         BEGIN_AUTO_LOGIN,
         BEGIN_VERIFICATION,
         BEGIN_VERIFICATION_FOR_USER,
@@ -246,9 +247,10 @@ gdm_greeter_server_reset (GdmGreeterServer *greeter_server)
 }
 
 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;
 }
 
@@ -323,6 +325,34 @@ 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)
@@ -645,7 +675,9 @@ greeter_handle_child_message (DBusConnection *connection,
 {
         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,7 +731,11 @@ do_introspect (DBusConnection *connection,
         /* interface */
         xml = g_string_append (xml,
                                "  <interface name=\"org.gnome.DisplayManager.GreeterServer\">\n"
+                               "    <method name=\"StartConversation\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
                                "    <method name=\"BeginVerification\">\n"
+                               "    </method>\n"
                                "    <method name=\"BeginTimedLogin\">\n"
                                "    </method>\n"
                                "    <method name=\"BeginVerificationForUser\">\n"
@@ -759,6 +795,7 @@ do_introspect (DBusConnection *connection,
                                "      <arg name=\"delay\" type=\"i\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Ready\">\n"
+                               "      <arg name=\"service-name\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Reset\">\n"
                                "    </signal>\n"
@@ -1121,6 +1158,16 @@ gdm_greeter_server_class_init (GdmGreeterServerClass *klass)
                                                               "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),
diff --git a/daemon/gdm-greeter-server.h b/daemon/gdm-greeter-server.h
index 6e92100..7333db1 100644
--- a/daemon/gdm-greeter-server.h
+++ b/daemon/gdm-greeter-server.h
@@ -45,6 +45,8 @@ 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_for_user)(GdmGreeterServer  *greeter_server,
@@ -85,7 +87,8 @@ gboolean            gdm_greeter_server_info                  (GdmGreeterServer *
 gboolean            gdm_greeter_server_problem               (GdmGreeterServer *greeter_server,
                                                               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);
 void                gdm_greeter_server_selected_user_changed (GdmGreeterServer *greeter_server,
                                                               const char       *text);
 void                gdm_greeter_server_default_language_name_changed (GdmGreeterServer *greeter_server,
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 08f88b7..a42c38f 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -517,6 +517,7 @@ on_session_secret_info_query (GdmSession     *session,
 
 static void
 on_session_conversation_started (GdmSession     *session,
+                                 const char     *service_name,
                                  GdmSimpleSlave *slave)
 {
         gboolean res;
@@ -526,7 +527,8 @@ on_session_conversation_started (GdmSession     *session,
 
         g_debug ("GdmSimpleSlave: session 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");
                 }
@@ -542,8 +544,10 @@ on_session_conversation_started (GdmSession     *session,
                 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);
         }
 
@@ -775,6 +779,16 @@ on_greeter_session_died (GdmGreeterSession    *greeter,
 }
 
 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);
+        gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
+                                        service_name);
+}
+
+static void
 on_greeter_begin_verification (GdmGreeterServer *greeter_server,
                                GdmSimpleSlave   *slave)
 {
@@ -949,6 +963,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);
diff --git a/gui/simple-greeter/gdm-greeter-client.c b/gui/simple-greeter/gdm-greeter-client.c
index ed4dd95..8d061a4 100644
--- a/gui/simple-greeter/gdm-greeter-client.c
+++ b/gui/simple-greeter/gdm-greeter-client.c
@@ -237,11 +237,7 @@ static void
 on_ready (GdmGreeterClient *client,
           DBusMessage      *message)
 {
-        g_debug ("GdmGreeterClient: Ready");
-
-        g_signal_emit (client,
-                       gdm_greeter_client_signals[READY],
-                       0);
+        emit_string_signal_for_message (client, "Ready", message, READY);
 }
 
 static void
@@ -400,6 +396,14 @@ send_dbus_void_method (DBusConnection *connection,
 }
 
 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)
 {
@@ -879,9 +883,9 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               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[RESET] =
                 g_signal_new ("reset",
diff --git a/gui/simple-greeter/gdm-greeter-client.h b/gui/simple-greeter/gdm-greeter-client.h
index 08deabd..88b0281 100644
--- a/gui/simple-greeter/gdm-greeter-client.h
+++ b/gui/simple-greeter/gdm-greeter-client.h
@@ -84,13 +84,15 @@ GQuark             gdm_greeter_client_error_quark                    (void);
 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);
diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index e7d206a..cd0cbdf 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -85,6 +85,7 @@ on_problem (GdmGreeterClient  *client,
 
 static void
 on_ready (GdmGreeterClient  *client,
+          const char        *service_name,
           GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: Ready");
@@ -270,6 +271,7 @@ on_cancelled (GdmGreeterLoginWindow *login_window,
 {
         gdm_greeter_panel_hide_user_options (GDM_GREETER_PANEL (session->priv->panel));
         gdm_greeter_client_call_cancel (session->priv->client);
+        gdm_greeter_client_call_start_conversation (session->priv->client, "gdm");
 }
 
 static void
@@ -439,6 +441,8 @@ gdm_greeter_session_start (GdmGreeterSession *session,
         toggle_panel (session, TRUE);
         toggle_login_window (session, TRUE);
 
+        gdm_greeter_client_call_start_conversation (session->priv->client, "gdm");
+
         gdm_profile_end (NULL);
 
         return res;
-- 
1.7.0.1


From ab835c20419bcc61b4b60e8fc6c2d00f6a267496 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 4 Mar 2009 22:09:21 -0500
Subject: [PATCH 04/47] start autologin conversation when creating session if necessary

Without this autologin breaks, since when it comes time to
autologin, there's no worker to do it.
---
 daemon/gdm-simple-slave.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index a42c38f..9c3eb36 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -1076,8 +1076,18 @@ idle_connect_to_display (GdmSimpleSlave *slave)
                 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) {
-- 
1.7.0.1


From d25daddaa06df47927d9d7797bb1fdaeedca724f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 16 Jan 2009 15:18:31 -0500
Subject: [PATCH 05/47] Store multiple conversations in the session

We keep multiple conversations in the session now, keyed off of
PAM service is at the other end.  Much of the guts still
only operate on the first conversation added though.
---
 daemon/gdm-session-direct.c |  123 +++++++++++++++++++++++++++++++++++++------
 1 files changed, 106 insertions(+), 17 deletions(-)

diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index abc3c9d..5281b2b 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -89,6 +89,7 @@ struct _GdmSessionDirectPrivate
         char                *user_x11_authority_file;
 
         GdmSessionConversation *conversation;
+        GHashTable          *conversations;
 
         GdmSessionWorkerJob *job;
         GPid                 session_pid;
@@ -1549,6 +1550,42 @@ allow_user_function (DBusConnection *connection,
         return FALSE;
 }
 
+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 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 void
 handle_connection (DBusServer      *server,
                    DBusConnection  *new_connection,
@@ -1556,10 +1593,22 @@ handle_connection (DBusServer      *server,
 {
         GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
         GdmSessionConversation *conversation;
+        gulong pid;
 
         g_debug ("GdmSessionDirect: Handing new connection");
 
-        conversation = session->priv->conversation;
+        if (!dbus_connection_get_unix_process_id (new_connection, &pid)) {
+                g_warning ("Unable to read pid on new worker connection");
+                return;
+        }
+
+        conversation = find_conversation_by_pid (session, (GPid) pid);
+
+        if (conversation == NULL) {
+                g_warning ("New worker connection is from unknown source");
+                return;
+        }
+
         if (conversation->worker_connection == NULL) {
                 DBusObjectPathVTable vtable = { &session_unregister_handler,
                                                 &session_message_handler,
@@ -1632,6 +1681,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,
@@ -1655,6 +1715,11 @@ gdm_session_direct_init (GdmSessionDirect *session)
                           G_CALLBACK (on_session_exited),
                           NULL);
 
+        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,
@@ -1754,17 +1819,15 @@ stop_conversation (GdmSessionConversation *conversation)
                                               G_CALLBACK (worker_died),
                                               conversation);
 
-        cancel_pending_query (conversation);
-
         if (conversation->worker_connection != NULL) {
                 dbus_connection_close (conversation->worker_connection);
                 conversation->worker_connection = NULL;
         }
 
         gdm_session_worker_job_stop (conversation->job);
+
         g_object_unref (conversation->job);
-        g_free (conversation->service_name);
-        g_free (conversation);
+        conversation->job = NULL;
 }
 
 static void
@@ -1772,12 +1835,20 @@ 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");
 
-        impl->priv->conversation = start_conversation (impl, service_name);
+        conversation = start_conversation (impl, service_name);
+
+        g_hash_table_insert (impl->priv->conversations,
+                             g_strdup (service_name), conversation);
+
+        if (impl->priv->conversation != NULL) {
+                impl->priv->conversation = conversation;
+        }
 }
 
 static void
@@ -1828,8 +1899,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);
 
-        conversation = session->priv->conversation;
-        if (! send_dbus_message (conversation, 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");
         }
 
@@ -1891,8 +1962,8 @@ send_setup_for_user (GdmSessionDirect *session,
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &display_x11_authority_file);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &selected_user);
 
-        conversation = session->priv->conversation;
-        if (! send_dbus_message (conversation, 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");
         }
 
@@ -1906,8 +1977,6 @@ gdm_session_direct_setup (GdmSession *session,
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (impl->priv->conversation != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
         send_setup (impl, service_name);
         gdm_session_direct_defaults_changed (impl);
@@ -1921,8 +1990,6 @@ gdm_session_direct_setup_for_user (GdmSession *session,
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (impl->priv->conversation != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
         g_return_if_fail (username != NULL);
 
         gdm_session_direct_select_user (session, username);
@@ -2170,6 +2237,28 @@ gdm_session_direct_start_session (GdmSession *session)
 }
 
 static void
+stop_all_conversations (GdmSessionDirect *session)
+{
+        GHashTableIter iter;
+        gpointer key, value;
+
+        if (session->priv->conversations == NULL) {
+                return;
+        }
+
+        g_hash_table_iter_init (&iter, session->priv->conversations);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionConversation *conversation;
+
+                conversation = (GdmSessionConversation *) value;
+
+                stop_conversation (conversation);
+        }
+
+        g_hash_table_remove_all (session->priv->conversations);
+}
+
+static void
 gdm_session_direct_close (GdmSession *session)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
@@ -2186,6 +2275,8 @@ gdm_session_direct_close (GdmSession *session)
                                            impl->priv->display_device);
         }
 
+        stop_all_conversations (impl);
+
         g_free (impl->priv->selected_user);
         impl->priv->selected_user = NULL;
 
@@ -2233,11 +2324,9 @@ gdm_session_direct_answer_query  (GdmSession *session,
 static void
 gdm_session_direct_cancel  (GdmSession *session)
 {
-        GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-
         g_return_if_fail (session != NULL);
 
-        cancel_pending_query (impl->priv->conversation);
+        stop_all_conversations (GDM_SESSION_DIRECT (session));
 }
 
 char *
-- 
1.7.0.1


From 8160da651f6d638cd4b75a8bfb8ae5b83fa4e593 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 22 Jan 2009 08:52:01 -0500
Subject: [PATCH 06/47] Propagate service name to more layers

This is more prep work to get multiple concurrent
PAM stacks going.
---
 daemon/gdm-factory-slave.c               |   69 +++--
 daemon/gdm-greeter-server.c              |  120 ++++++--
 daemon/gdm-greeter-server.h              |   17 +-
 daemon/gdm-product-slave.c               |  246 ++++++++++++---
 daemon/gdm-session-direct.c              |  498 +++++++++++++++++------------
 daemon/gdm-session-private.h             |   26 ++-
 daemon/gdm-session-relay.c               |  135 +++++++--
 daemon/gdm-session-worker.c              |   27 ++
 daemon/gdm-session.c                     |  175 ++++++-----
 daemon/gdm-session.h                     |   54 +++-
 daemon/gdm-simple-slave.c                |   78 ++++--
 daemon/test-session.c                    |   22 +-
 gui/simple-greeter/gdm-greeter-client.c  |  188 +++++++++---
 gui/simple-greeter/gdm-greeter-client.h  |   16 +-
 gui/simple-greeter/gdm-greeter-session.c |   11 +-
 15 files changed, 1191 insertions(+), 491 deletions(-)

diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c
index ca3ba48..553ae3a 100644
--- a/daemon/gdm-factory-slave.c
+++ b/daemon/gdm-factory-slave.c
@@ -144,45 +144,49 @@ on_greeter_session_died (GdmGreeterSession    *greeter,
 
 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_conversation_started (GdmSession      *session,
-                                 GdmFactorySlave *slave,
-                                 const char      *service_name)
+                                 const char      *service_name,
+                                 GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave: session conversation started");
 
@@ -192,17 +196,19 @@ on_session_conversation_started (GdmSession      *session,
 
 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);
 }
@@ -224,23 +230,26 @@ on_session_reset_failed (GdmSession      *session,
 
 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;
@@ -248,60 +257,65 @@ on_session_authorized (GdmSession      *session,
         /* 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_open_session (session);
+        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);
+        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, _("Unable to open session"));
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to open session"));
 
         queue_greeter_reset (slave);
 }
@@ -389,37 +403,48 @@ on_session_relay_connected (GdmSessionRelay *session,
                             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_start_conversation (GDM_SESSION (slave->priv->session), "gdm");
+        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
@@ -516,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);
diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index ffca7ec..dae34c5 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -43,6 +43,7 @@
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
+#include "gdm-marshal.h"
 #include "gdm-greeter-server.h"
 
 #define GDM_GREETER_SERVER_DBUS_PATH      "/org/gnome/DisplayManager/GreeterServer"
@@ -156,6 +157,46 @@ send_dbus_string_and_int_signal (GdmGreeterServer *greeter_server,
 }
 
 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)
@@ -208,34 +249,38 @@ send_dbus_void_signal (GdmGreeterServer *greeter_server,
 
 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;
 }
 
@@ -291,9 +336,10 @@ gdm_greeter_server_request_timed_login (GdmGreeterServer *greeter_server,
 }
 
 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
@@ -358,6 +404,16 @@ handle_begin_verification (GdmGreeterServer *greeter_server,
                            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");
 
@@ -365,7 +421,7 @@ handle_begin_verification (GdmGreeterServer *greeter_server,
         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;
 }
@@ -379,7 +435,6 @@ handle_begin_auto_login (GdmGreeterServer *greeter_server,
         DBusError    error;
         const char  *text;
 
-
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
                                      DBUS_TYPE_STRING, &text,
@@ -406,13 +461,16 @@ handle_begin_verification_for_user (GdmGreeterServer *greeter_server,
         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);
 
@@ -420,7 +478,7 @@ handle_begin_verification_for_user (GdmGreeterServer *greeter_server,
         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;
 }
@@ -433,13 +491,16 @@ handle_answer_query (GdmGreeterServer *greeter_server,
         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");
 
@@ -447,7 +508,7 @@ handle_answer_query (GdmGreeterServer *greeter_server,
         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;
 }
@@ -644,9 +705,11 @@ handle_start_session_when_ready (GdmGreeterServer *greeter_server,
         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);
@@ -660,9 +723,9 @@ handle_start_session_when_ready (GdmGreeterServer *greeter_server,
         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;
@@ -735,13 +798,16 @@ do_introspect (DBusConnection *connection,
                                "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"BeginVerification\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"BeginTimedLogin\">\n"
                                "    </method>\n"
                                "    <method name=\"BeginVerificationForUser\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"username\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"AnswerQuery\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"SelectSession\">\n"
@@ -764,18 +830,23 @@ do_introspect (DBusConnection *connection,
                                "      <arg name=\"id\" direction=\"out\" type=\"o\"/>\n"
                                "    </method>\n"
                                "    <method name=\"StartSessionWhenReady\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"should_start_session\" type=\"b\"/>\n"
                                "    </method>\n"
                                "    <signal name=\"Info\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Problem\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"InfoQuery\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"SecretInfoQuery\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"SelectedUserChanged\">\n"
@@ -800,6 +871,7 @@ do_introspect (DBusConnection *connection,
                                "    <signal name=\"Reset\">\n"
                                "    </signal>\n"
                                "    <signal name=\"UserAuthorized\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "  </interface>\n");
 
@@ -1175,9 +1247,9 @@ gdm_greeter_server_class_init (GdmGreeterServerClass *klass)
                               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),
@@ -1196,10 +1268,10 @@ gdm_greeter_server_class_init (GdmGreeterServerClass *klass)
                               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),
@@ -1207,10 +1279,10 @@ gdm_greeter_server_class_init (GdmGreeterServerClass *klass)
                               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),
@@ -1304,9 +1376,9 @@ gdm_greeter_server_class_init (GdmGreeterServerClass *klass)
                               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",
@@ -1315,9 +1387,9 @@ gdm_greeter_server_class_init (GdmGreeterServerClass *klass)
                               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 --git a/daemon/gdm-greeter-server.h b/daemon/gdm-greeter-server.h
index 7333db1..6d0dd87 100644
--- a/daemon/gdm-greeter-server.h
+++ b/daemon/gdm-greeter-server.h
@@ -48,10 +48,13 @@ typedef struct
         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);
@@ -66,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;
 
@@ -77,14 +81,17 @@ gboolean            gdm_greeter_server_start                 (GdmGreeterServer *
 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,
@@ -101,8 +108,8 @@ void                gdm_greeter_server_default_session_name_changed (GdmGreeterS
 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 --git a/daemon/gdm-product-slave.c b/daemon/gdm-product-slave.c
index ec6300d..93d83a1 100644
--- a/daemon/gdm-product-slave.c
+++ b/daemon/gdm-product-slave.c
@@ -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_finalize      (GObject             *object);
 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)
@@ -356,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;
 }
@@ -508,114 +573,133 @@ on_session_reset_failed (GdmSession      *session,
 
 static void
 on_session_authenticated (GdmSession      *session,
+                          const char      *service_name,
                           GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Authenticated");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "Authenticated", service_name);
 }
 
 static void
 on_session_authentication_failed (GdmSession      *session,
+                                  const char      *service_name,
                                   const char      *message,
                                   GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "AuthenticationFailed",
-                                 message);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "AuthenticationFailed",
+                                        service_name,
+                                        message);
 }
 
 static void
 on_session_authorized (GdmSession      *session,
+                       const char      *service_name,
                        GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Authorized");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "Authorized", service_name);
 }
 
 static void
 on_session_authorization_failed (GdmSession      *session,
+                                 const char      *service_name,
                                  const char      *message,
                                  GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "AuthorizationFailed",
-                                 message);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "AuthorizationFailed",
+                                        service_name,
+                                        message);
 }
 
 static void
 on_session_accredited (GdmSession      *session,
+                       const char      *service_name,
                        GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Accredited");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "Accredited", service_name);
 }
 
 static void
 on_session_accreditation_failed (GdmSession      *session,
+                                 const char      *service_name,
                                  const char      *message,
                                  GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "AccreditationFailed",
-                                 message);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "AccreditationFailed",
+                                        service_name,
+                                        message);
 }
 
 static void
 on_session_opened (GdmSession      *session,
+                   const char      *service_name,
                    GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "SessionOpened");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "SessionOpened", service_name);
 }
 
 static void
 on_session_open_failed (GdmSession      *session,
+                        const char      *service_name,
                         const char      *message,
                         GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "SessionOpenFailed",
-                                 message);
+        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
@@ -676,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
@@ -714,16 +854,18 @@ on_relay_answer_query (GdmProductSlave *slave,
 {
         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);
@@ -831,7 +973,25 @@ 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
diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 5281b2b..168bf73 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -88,10 +88,10 @@ struct _GdmSessionDirectPrivate
         char                *selected_user;
         char                *user_x11_authority_file;
 
-        GdmSessionConversation *conversation;
         GHashTable          *conversations;
 
-        GdmSessionWorkerJob *job;
+        GList               *pending_connections;
+
         GPid                 session_pid;
         guint32              is_running : 1;
 
@@ -198,14 +198,30 @@ send_dbus_void_signal (GdmSessionConversation *conversation,
         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);
         GdmSessionConversation *conversation;
 
-        conversation = impl->priv->conversation;
+        conversation = find_conversation_by_name (impl, service_name);
         if (conversation != NULL) {
                 gdm_session_record_failed (conversation->worker_pid,
                                            impl->priv->selected_user,
@@ -216,12 +232,13 @@ on_authentication_failed (GdmSession *session,
 }
 
 static void
-on_session_started (GdmSession *session)
+on_session_started (GdmSession *session,
+                    const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
         GdmSessionConversation *conversation;
 
-        conversation = impl->priv->conversation;
+        conversation = find_conversation_by_name (impl, service_name);
         if (conversation != NULL) {
                 gdm_session_record_login (conversation->worker_pid,
                                           impl->priv->selected_user,
@@ -233,12 +250,13 @@ on_session_started (GdmSession *session)
 
 static void
 on_session_start_failed (GdmSession *session,
+                         const char *service_name,
                          const char *message)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
         GdmSessionConversation *conversation;
 
-        conversation = impl->priv->conversation;
+        conversation = find_conversation_by_name (impl, service_name);
         if (conversation != NULL) {
                 gdm_session_record_login (conversation->worker_pid,
                                           impl->priv->selected_user,
@@ -263,7 +281,7 @@ on_session_exited (GdmSession *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_setup_complete (GdmSessionDirect *session,
-                                          DBusConnection   *connection,
+                                          GdmSessionConversation *conversation,
                                           DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -271,17 +289,17 @@ gdm_session_direct_handle_setup_complete (GdmSessionDirect *session,
         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;
@@ -296,12 +314,12 @@ gdm_session_direct_handle_setup_failed (GdmSessionDirect *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);
 
         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;
 }
@@ -309,7 +327,7 @@ gdm_session_direct_handle_setup_failed (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_reset_complete (GdmSessionDirect *session,
-                                          DBusConnection   *connection,
+                                          GdmSessionConversation *conversation,
                                           DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -317,7 +335,7 @@ gdm_session_direct_handle_reset_complete (GdmSessionDirect *session,
         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));
@@ -327,7 +345,7 @@ gdm_session_direct_handle_reset_complete (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_reset_failed (GdmSessionDirect *session,
-                                        DBusConnection   *connection,
+                                        GdmSessionConversation *conversation,
                                         DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -342,7 +360,7 @@ gdm_session_direct_handle_reset_failed (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: Emitting 'reset-failed' signal");
@@ -354,7 +372,7 @@ gdm_session_direct_handle_reset_failed (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_authenticated (GdmSessionDirect *session,
-                                         DBusConnection   *connection,
+                                         GdmSessionConversation *conversation,
                                          DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -362,17 +380,17 @@ gdm_session_direct_handle_authenticated (GdmSessionDirect *session,
         g_debug ("GdmSessionDirect: Emitting 'authenticated' signal");
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authenticated (GDM_SESSION (session));
+        _gdm_session_authenticated (GDM_SESSION (session), conversation->service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_authentication_failed (GdmSessionDirect *session,
-                                                 DBusConnection   *connection,
+                                                 GdmSessionConversation *conversation,
                                                  DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -387,19 +405,19 @@ gdm_session_direct_handle_authentication_failed (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: Emitting 'authentication-failed' signal");
 
-        _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;
@@ -407,17 +425,17 @@ gdm_session_direct_handle_authorized (GdmSessionDirect *session,
         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;
@@ -432,19 +450,19 @@ gdm_session_direct_handle_authorization_failed (GdmSessionDirect *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);
 
         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;
@@ -452,17 +470,17 @@ gdm_session_direct_handle_accredited (GdmSessionDirect *session,
         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;
@@ -477,12 +495,12 @@ gdm_session_direct_handle_accreditation_failed (GdmSessionDirect *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);
 
         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;
 }
@@ -764,7 +782,7 @@ gdm_session_direct_select_user (GdmSession *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_username_changed (GdmSessionDirect *session,
-                                            DBusConnection   *connection,
+                                            GdmSessionConversation *conversation,
                                             DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -779,7 +797,7 @@ gdm_session_direct_handle_username_changed (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: changing username from '%s' to '%s'",
@@ -846,14 +864,11 @@ set_pending_query (GdmSessionConversation *conversation,
 
 static DBusHandlerResult
 gdm_session_direct_handle_info_query (GdmSessionDirect *session,
-                                      DBusConnection   *connection,
+                                      GdmSessionConversation *conversation,
                                       DBusMessage      *message)
 {
         DBusError    error;
         const char  *text;
-        GdmSessionConversation *conversation;
-
-        conversation = session->priv->conversation;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
@@ -865,21 +880,18 @@ gdm_session_direct_handle_info_query (GdmSessionDirect *session,
         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;
         const char  *text;
-        GdmSessionConversation *conversation;
-
-        conversation = session->priv->conversation;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
@@ -891,14 +903,14 @@ gdm_session_direct_handle_secret_info_query (GdmSessionDirect *session,
         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;
@@ -913,31 +925,28 @@ gdm_session_direct_handle_info (GdmSessionDirect *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);
 
         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;
-        GdmSessionConversation *conversation;
 
         g_debug ("GdmSessionDirect: worker cancelling pending query");
 
-        conversation = session->priv->conversation;
-
         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;
@@ -945,7 +954,7 @@ gdm_session_direct_handle_cancel_pending_query (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_problem (GdmSessionDirect *session,
-                                   DBusConnection   *connection,
+                                   GdmSessionConversation *conversation,
                                    DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -960,18 +969,18 @@ gdm_session_direct_handle_problem (GdmSessionDirect *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);
 
         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,
-                                          DBusConnection   *connection,
+                                          GdmSessionConversation *conversation,
                                           DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -986,10 +995,10 @@ gdm_session_direct_handle_session_opened (GdmSessionDirect *session,
 
         g_debug ("GdmSessionDirect: Emitting 'session-opened' signal");
 
-        _gdm_session_session_opened (GDM_SESSION (session));
+        _gdm_session_session_opened (GDM_SESSION (session), conversation->service_name);
 
         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;
@@ -997,7 +1006,7 @@ gdm_session_direct_handle_session_opened (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_open_failed (GdmSessionDirect *session,
-                                       DBusConnection   *connection,
+                                       GdmSessionConversation *conversation,
                                        DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1012,18 +1021,18 @@ gdm_session_direct_handle_open_failed (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: Emitting 'session-open-failed' signal");
-        _gdm_session_session_open_failed (GDM_SESSION (session), text);
+        _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;
@@ -1042,7 +1051,7 @@ gdm_session_direct_handle_session_started (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: Emitting 'session-started' signal with pid '%d'",
@@ -1051,14 +1060,14 @@ gdm_session_direct_handle_session_started (GdmSessionDirect *session,
         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;
@@ -1073,18 +1082,18 @@ gdm_session_direct_handle_start_failed (GdmSessionDirect *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);
 
         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;
@@ -1099,7 +1108,7 @@ gdm_session_direct_handle_session_exited (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: Emitting 'session-exited' signal with exit code '%d'",
@@ -1113,7 +1122,7 @@ gdm_session_direct_handle_session_exited (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_session_died (GdmSessionDirect *session,
-                                        DBusConnection   *connection,
+                                        GdmSessionConversation *conversation,
                                         DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1128,7 +1137,7 @@ gdm_session_direct_handle_session_died (GdmSessionDirect *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);
 
         g_debug ("GdmSessionDirect: Emitting 'session-died' signal with signal number '%d'",
@@ -1142,7 +1151,7 @@ gdm_session_direct_handle_session_died (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_saved_language_name_read (GdmSessionDirect *session,
-                                                    DBusConnection   *connection,
+                                                    GdmSessionConversation *conversation,
                                                     DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1157,7 +1166,7 @@ gdm_session_direct_handle_saved_language_name_read (GdmSessionDirect *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 (strcmp (language_name,
@@ -1175,7 +1184,7 @@ gdm_session_direct_handle_saved_language_name_read (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_saved_layout_name_read (GdmSessionDirect *session,
-                                                  DBusConnection   *connection,
+                                                  GdmSessionConversation *conversation,
                                                   DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1190,7 +1199,7 @@ gdm_session_direct_handle_saved_layout_name_read (GdmSessionDirect *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 (strcmp (layout_name,
@@ -1208,7 +1217,7 @@ gdm_session_direct_handle_saved_layout_name_read (GdmSessionDirect *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_saved_session_name_read (GdmSessionDirect *session,
-                                                   DBusConnection   *connection,
+                                                   GdmSessionConversation *conversation,
                                                    DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1223,7 +1232,7 @@ gdm_session_direct_handle_saved_session_name_read (GdmSessionDirect *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)) {
@@ -1251,58 +1260,61 @@ session_worker_message (DBusConnection *connection,
                         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, connection, message);
+                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, connection, message);
+                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;
@@ -1536,6 +1548,27 @@ session_unregister_handler (DBusConnection  *connection,
         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,
@@ -1550,40 +1583,85 @@ allow_user_function (DBusConnection *connection,
         return FALSE;
 }
 
-static GdmSessionConversation *
-find_conversation_by_name (GdmSessionDirect *session,
-                           const char       *service_name)
+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;
 
-        conversation = g_hash_table_lookup (session->priv->conversations, service_name);
+        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 ("Tried to look up non-existant conversation");
+                g_warning ("GdmSessionDirect: New worker connection is from unknown source");
+                dbus_connection_unref (connection);
+                return FALSE;
         }
 
-        return conversation;
+        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 GdmSessionConversation *
-find_conversation_by_pid (GdmSessionDirect *session,
-                          GPid              pid)
+static DBusHandlerResult
+on_message (DBusConnection *connection,
+            DBusMessage    *message,
+            void           *user_data)
 {
-        GHashTableIter iter;
-        gpointer key, value;
+        GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
 
-        g_hash_table_iter_init (&iter, session->priv->conversations);
-        while (g_hash_table_iter_next (&iter, &key, &value)) {
-                GdmSessionConversation *conversation;
+        g_debug ("GdmSessionDirect: got message");
 
-                conversation = (GdmSessionConversation *) value;
+        if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Hello")) {
+                DBusMessage *reply;
 
-                if (conversation->worker_pid == pid) {
-                        return conversation;
+                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 NULL;
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 static void
@@ -1592,50 +1670,23 @@ handle_connection (DBusServer      *server,
                    void            *user_data)
 {
         GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
-        GdmSessionConversation *conversation;
-        gulong pid;
-
         g_debug ("GdmSessionDirect: Handing new connection");
 
-        if (!dbus_connection_get_unix_process_id (new_connection, &pid)) {
-                g_warning ("Unable to read pid on new worker connection");
-                return;
-        }
-
-        conversation = find_conversation_by_pid (session, (GPid) pid);
-
-        if (conversation == NULL) {
-                g_warning ("New worker connection is from unknown source");
-                return;
-        }
-
-        if (conversation->worker_connection == NULL) {
-                DBusObjectPathVTable vtable = { &session_unregister_handler,
-                                                &session_message_handler,
-                                                NULL, NULL, NULL, NULL
-                };
-
-                conversation->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);
+        /* 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);
 
-                g_debug ("GdmSessionDirect: Emitting conversation-started signal");
-                _gdm_session_conversation_started (GDM_SESSION (session),
-                                                   conversation->service_name);
-        }
+        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
@@ -1820,6 +1871,8 @@ stop_conversation (GdmSessionConversation *conversation)
                                               conversation);
 
         if (conversation->worker_connection != NULL) {
+                dbus_connection_remove_filter (conversation->worker_connection, on_message, session);
+
                 dbus_connection_close (conversation->worker_connection);
                 conversation->worker_connection = NULL;
         }
@@ -1845,10 +1898,6 @@ gdm_session_direct_start_conversation (GdmSession *session,
 
         g_hash_table_insert (impl->priv->conversations,
                              g_strdup (service_name), conversation);
-
-        if (impl->priv->conversation != NULL) {
-                impl->priv->conversation = conversation;
-        }
 }
 
 static void
@@ -1999,45 +2048,50 @@ gdm_session_direct_setup_for_user (GdmSession *session,
 }
 
 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 (impl->priv->conversation != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
-        conversation = impl->priv->conversation;
-        send_dbus_void_signal (conversation, "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 (impl->priv->conversation != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
-        conversation = impl->priv->conversation;
-        send_dbus_void_signal (conversation, "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 (impl->priv->conversation != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->conversation->worker_connection));
 
-        conversation = impl->priv->conversation;
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation == NULL) {
+                return;
+        }
+
         switch (cred_flag) {
         case GDM_SESSION_CRED_ESTABLISH:
                 send_dbus_void_signal (conversation, "EstablishCredentials");
@@ -2051,13 +2105,12 @@ gdm_session_direct_accredit (GdmSession *session,
 }
 
 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;
-        GdmSessionConversation *conversation;
 
         message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
                                            GDM_SESSION_DBUS_INTERFACE,
@@ -2067,7 +2120,6 @@ send_environment_variable (const char       *key,
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);
 
-        conversation = session->priv->conversation;
         if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "SetEnvironmentVariable");
         }
@@ -2076,12 +2128,13 @@ send_environment_variable (const char       *key,
 }
 
 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 *
@@ -2198,17 +2251,22 @@ setup_session_environment (GdmSessionDirect *session)
 }
 
 static void
-gdm_session_direct_open_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);
 
-        send_dbus_void_signal (impl, "OpenSession");
+        conversation = find_conversation_by_name (impl, service_name);
+
+        send_dbus_string_signal (conversation, "OpenSession", service_name);
 }
 
 static void
-gdm_session_direct_start_session (GdmSession *session)
+gdm_session_direct_start_session (GdmSession *session,
+                                  const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
         GdmSessionConversation *conversation;
@@ -2228,10 +2286,11 @@ gdm_session_direct_start_session (GdmSession *session)
 
         g_free (command);
 
+        conversation = find_conversation_by_name (impl, service_name);
+
         setup_session_environment (impl);
-        send_environment (impl);
+        send_environment (impl, conversation);
 
-        conversation = impl->priv->conversation;
         send_dbus_string_signal (conversation, "StartProgram", program);
         g_free (program);
 }
@@ -2277,6 +2336,11 @@ gdm_session_direct_close (GdmSession *session)
 
         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;
 
@@ -2309,6 +2373,7 @@ gdm_session_direct_close (GdmSession *session)
 
 static void
 gdm_session_direct_answer_query  (GdmSession *session,
+                                  const char *service_name,
                                   const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
@@ -2316,7 +2381,7 @@ gdm_session_direct_answer_query  (GdmSession *session,
 
         g_return_if_fail (session != NULL);
 
-        conversation = impl->priv->conversation;
+        conversation = find_conversation_by_name (impl, service_name);
 
         answer_pending_query (conversation, text);
 }
@@ -2391,7 +2456,8 @@ gdm_session_direct_select_session (GdmSession *session,
                                    const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-        GdmSessionConversation *conversation;
+        GHashTableIter iter;
+        gpointer key, value;
 
         g_free (impl->priv->selected_session);
 
@@ -2401,9 +2467,15 @@ gdm_session_direct_select_session (GdmSession *session,
                 impl->priv->selected_session = g_strdup (text);
         }
 
-        conversation = impl->priv->conversation;
-        send_dbus_string_signal (conversation, "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
@@ -2411,7 +2483,8 @@ gdm_session_direct_select_language (GdmSession *session,
                                     const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-        GdmSessionConversation *conversation;
+        GHashTableIter iter;
+        gpointer key, value;
 
         g_free (impl->priv->selected_language);
 
@@ -2421,9 +2494,15 @@ gdm_session_direct_select_language (GdmSession *session,
                 impl->priv->selected_language = g_strdup (text);
         }
 
-        conversation = impl->priv->conversation;
-        send_dbus_string_signal (conversation, "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
@@ -2431,7 +2510,8 @@ gdm_session_direct_select_layout (GdmSession *session,
                                   const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-        GdmSessionConversation *conversation;
+        GHashTableIter iter;
+        gpointer key, value;
 
         g_free (impl->priv->selected_layout);
 
@@ -2441,9 +2521,15 @@ gdm_session_direct_select_layout (GdmSession *session,
                 impl->priv->selected_layout = g_strdup (text);
         }
 
-        conversation = impl->priv->conversation;
-        send_dbus_string_signal (conversation, "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
diff --git a/daemon/gdm-session-private.h b/daemon/gdm-session-private.h
index 74b6069..4dc6e44 100644
--- a/daemon/gdm-session-private.h
+++ b/daemon/gdm-session-private.h
@@ -29,27 +29,39 @@ G_BEGIN_DECLS
 /* state changes */
 void             _gdm_session_conversation_started         (GdmSession   *session,
                                                             const char   *service_name);
-void             _gdm_session_setup_complete               (GdmSession   *session);
+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);
+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);
@@ -70,12 +82,16 @@ void             _gdm_session_selected_user_changed        (GdmSession   *sessio
 
 /* 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 --git a/daemon/gdm-session-relay.c b/daemon/gdm-session-relay.c
index a0b4cbf..98ccc51 100644
--- a/daemon/gdm-session-relay.c
+++ b/daemon/gdm-session-relay.c
@@ -212,31 +212,34 @@ gdm_session_relay_setup_for_user (GdmSession *session,
 }
 
 static void
-gdm_session_relay_authenticate (GdmSession *session)
+gdm_session_relay_authenticate (GdmSession *session,
+                                const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "Authenticate");
+        send_dbus_string_signal (impl, "Authenticate", service_name);
 }
 
 static void
-gdm_session_relay_authorize (GdmSession *session)
+gdm_session_relay_authorize (GdmSession *session,
+                             const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "Authorize");
+        send_dbus_string_signal (impl, "Authorize", service_name);
 }
 
 static void
 gdm_session_relay_accredit (GdmSession *session,
+                            const char *service_name,
                             int         cred_flag)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
 
         switch (cred_flag) {
         case GDM_SESSION_CRED_ESTABLISH:
-                send_dbus_void_signal (impl, "EstablishCredentials");
+                send_dbus_string_signal (impl, "EstablishCredentials", service_name);
                 break;
         case GDM_SESSION_CRED_REFRESH:
-                send_dbus_void_signal (impl, "RefreshCredentials");
+                send_dbus_string_signal (impl, "RefreshCredentials", service_name);
                 break;
         default:
                 g_assert_not_reached ();
@@ -244,18 +247,20 @@ gdm_session_relay_accredit (GdmSession *session,
 }
 
 static void
-gdm_session_relay_open_session (GdmSession *session)
+gdm_session_relay_open_session (GdmSession *session,
+                                const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "OpenSession");
+        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
@@ -299,11 +304,12 @@ gdm_session_relay_cancel (GdmSession *session)
 }
 
 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
@@ -341,10 +347,12 @@ handle_info_query (GdmSessionRelay *session_relay,
 {
         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);
@@ -356,7 +364,7 @@ handle_info_query (GdmSessionRelay *session_relay,
         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;
 }
@@ -368,12 +376,14 @@ handle_secret_info_query (GdmSessionRelay *session_relay,
 {
         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);
@@ -385,7 +395,7 @@ handle_secret_info_query (GdmSessionRelay *session_relay,
         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;
 }
@@ -397,12 +407,14 @@ handle_info (GdmSessionRelay *session_relay,
 {
         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);
@@ -414,7 +426,7 @@ handle_info (GdmSessionRelay *session_relay,
         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;
 }
@@ -426,12 +438,14 @@ handle_problem (GdmSessionRelay *session_relay,
 {
         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);
@@ -443,7 +457,7 @@ handle_problem (GdmSessionRelay *session_relay,
         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;
 }
@@ -455,8 +469,15 @@ handle_setup_complete (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -464,7 +485,7 @@ handle_setup_complete (GdmSessionRelay *session_relay,
         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;
 }
@@ -476,8 +497,15 @@ handle_setup_failed (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -485,7 +513,7 @@ handle_setup_failed (GdmSessionRelay *session_relay,
         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;
 }
@@ -498,8 +526,15 @@ handle_authenticated (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -507,7 +542,7 @@ handle_authenticated (GdmSessionRelay *session_relay,
         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;
 }
@@ -519,8 +554,15 @@ handle_authentication_failed (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -528,7 +570,7 @@ handle_authentication_failed (GdmSessionRelay *session_relay,
         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;
 }
@@ -540,8 +582,15 @@ handle_authorized (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -549,7 +598,7 @@ handle_authorized (GdmSessionRelay *session_relay,
         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;
 }
@@ -561,8 +610,15 @@ handle_authorization_failed (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -570,7 +626,7 @@ handle_authorization_failed (GdmSessionRelay *session_relay,
         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;
 }
@@ -582,8 +638,15 @@ handle_accredited (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -591,7 +654,7 @@ handle_accredited (GdmSessionRelay *session_relay,
         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;
 }
@@ -603,8 +666,15 @@ handle_accreditation_failed (GdmSessionRelay *session_relay,
 {
         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");
 
@@ -612,7 +682,7 @@ handle_accreditation_failed (GdmSessionRelay *session_relay,
         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;
 }
@@ -623,9 +693,11 @@ handle_session_opened (GdmSessionRelay *session_relay,
 {
         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);
         }
@@ -637,7 +709,7 @@ handle_session_opened (GdmSessionRelay *session_relay,
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_session_opened (GDM_SESSION (session_relay));
+        _gdm_session_session_opened (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -649,9 +721,11 @@ handle_session_open_failed (GdmSessionRelay *session_relay,
 {
         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);
         }
@@ -663,7 +737,7 @@ handle_session_open_failed (GdmSessionRelay *session_relay,
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_session_open_failed (GDM_SESSION (session_relay), NULL);
+        _gdm_session_session_open_failed (GDM_SESSION (session_relay), service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -675,6 +749,7 @@ handle_session_started (GdmSessionRelay *session_relay,
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
         int          pid;
 
         dbus_error_init (&error);
@@ -682,6 +757,7 @@ handle_session_started (GdmSessionRelay *session_relay,
         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);
@@ -694,6 +770,7 @@ handle_session_started (GdmSessionRelay *session_relay,
         dbus_message_unref (reply);
 
         _gdm_session_session_started (GDM_SESSION (session_relay),
+                                      service_name,
                                       pid);
 
         return DBUS_HANDLER_RESULT_HANDLED;
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 5e34fb9..2f07e13 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -2876,6 +2876,28 @@ worker_dbus_filter_function (DBusConnection *connection,
         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,
@@ -2902,6 +2924,11 @@ gdm_session_worker_constructor (GType                  type,
                 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);
 
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 1820e29..a7b4e8b 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -24,6 +24,7 @@
 #include <glib/gi18n.h>
 #include <glib-object.h>
 
+#include "gdm-marshal.h"
 #include "gdm-session.h"
 #include "gdm-session-private.h"
 
@@ -116,37 +117,41 @@ gdm_session_setup_for_user (GdmSession *session,
 }
 
 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
@@ -194,19 +199,21 @@ gdm_session_cancel (GdmSession *session)
 }
 
 void
-gdm_session_open_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);
+        GDM_SESSION_GET_IFACE (session)->open_session (session, service_name);
 }
 
 void
-gdm_session_start_session (GdmSession *session)
+gdm_session_start_session (GdmSession *session,
+                           const char *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->start_session (session);
+        GDM_SESSION_GET_IFACE (session)->start_session (session, service_name);
 }
 
 static void
@@ -223,7 +230,7 @@ gdm_session_class_init (gpointer g_iface)
                               NULL,
                               g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
         signals [SETUP_COMPLETE] =
                 g_signal_new ("setup-complete",
                               iface_type,
@@ -231,9 +238,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,
@@ -241,10 +249,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,
@@ -273,9 +281,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,
@@ -283,10 +291,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,
@@ -294,9 +302,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,
@@ -304,10 +312,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,
@@ -315,9 +323,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,
@@ -325,10 +333,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",
@@ -337,10 +345,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,
@@ -348,10 +356,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,
@@ -359,10 +367,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,
@@ -370,10 +378,10 @@ gdm_session_class_init (gpointer g_iface)
                               G_STRUCT_OFFSET (GdmSessionIface, problem),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_OPENED] =
                 g_signal_new ("session-opened",
                               iface_type,
@@ -381,9 +389,10 @@ gdm_session_class_init (gpointer g_iface)
                               G_STRUCT_OFFSET (GdmSessionIface, session_opened),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1,
+                              G_TYPE_STRING);
         signals [SESSION_OPEN_FAILED] =
                 g_signal_new ("session-open-failed",
                               iface_type,
@@ -391,10 +400,10 @@ gdm_session_class_init (gpointer g_iface)
                               G_STRUCT_OFFSET (GdmSessionIface, session_open_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_STARTED] =
                 g_signal_new ("session-started",
                               iface_type,
@@ -402,10 +411,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,
@@ -413,10 +422,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,
@@ -496,19 +505,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
@@ -528,114 +539,128 @@ _gdm_session_reset_failed (GdmSession   *session,
 }
 
 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)
+_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);
+        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, text);
+        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
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index 00c2fa4..20a442b 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -53,12 +53,17 @@ struct _GdmSessionIface
                                               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);
+        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);
@@ -68,41 +73,58 @@ 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);
+        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);
@@ -132,15 +154,21 @@ 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_open_session                (GdmSession *session);
-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 --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 9c3eb36..e2a7877 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -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;
@@ -101,6 +103,7 @@ static void start_greeter      (GdmSimpleSlave *slave);
 
 static void
 on_session_started (GdmSession       *session,
+                    const char       *service_name,
                     int               pid,
                     GdmSimpleSlave   *slave)
 {
@@ -209,19 +212,22 @@ queue_greeter_reset (GdmSimpleSlave *slave)
 
 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);
@@ -245,18 +251,21 @@ on_session_reset_failed (GdmSession     *session,
 
 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);
@@ -264,7 +273,8 @@ on_session_authentication_failed (GdmSession     *session,
 }
 
 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;
@@ -285,7 +295,7 @@ gdm_simple_slave_accredit_when_ready (GdmSimpleSlave *slave)
                 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;
         }
@@ -293,25 +303,28 @@ gdm_simple_slave_accredit_when_ready (GdmSimpleSlave *slave)
 
 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);
@@ -397,31 +410,38 @@ start_session_timeout (GdmSimpleSlave *slave)
 
         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)
 {
-        gdm_session_open_session (session);
+        gdm_session_open_session (session, service_name);
 }
 
 static void
 on_session_accreditation_failed (GdmSession     *session,
+                                 const char     *service_name,
                                  const char     *message,
                                  GdmSimpleSlave *slave)
 {
@@ -442,6 +462,7 @@ on_session_accreditation_failed (GdmSession     *session,
                                 problem = _("Unable to establish credentials");
                         }
                         gdm_greeter_server_problem (slave->priv->greeter_server,
+                                                    service_name,
                                                     problem);
                 }
         }
@@ -457,62 +478,68 @@ on_session_accreditation_failed (GdmSession     *session,
 
 static void
 on_session_opened (GdmSession     *session,
+                   const char     *service_name,
                    GdmSimpleSlave *slave)
 {
-        queue_start_session (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"));
         }
 
-        destroy_session (slave);
-        queue_greeter_reset (slave);
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static void
 on_session_info (GdmSession     *session,
+                 const char     *service_name,
                  const char     *text,
                  GdmSimpleSlave *slave)
 {
         g_debug ("GdmSimpleSlave: Info: %s", text);
         if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_info (slave->priv->greeter_server, text);
+                gdm_greeter_server_info (slave->priv->greeter_server, service_name, text);
         }
 }
 
 static void
 on_session_problem (GdmSession     *session,
+                    const char     *service_name,
                     const char     *text,
                     GdmSimpleSlave *slave)
 {
         g_debug ("GdmSimpleSlave: Problem: %s", text);
-        gdm_greeter_server_problem (slave->priv->greeter_server, text);
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_info_query (GdmSession     *session,
+                       const char     *service_name,
                        const char     *text,
                        GdmSimpleSlave *slave)
 {
 
         g_debug ("GdmSimpleSlave: Info query: %s", text);
-        gdm_greeter_server_info_query (slave->priv->greeter_server, text);
+        gdm_greeter_server_info_query (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_secret_info_query (GdmSession     *session,
+                              const char     *service_name,
                               const char     *text,
                               GdmSimpleSlave *slave)
 {
         g_debug ("GdmSimpleSlave: Secret info query: %s", text);
-        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, text);
+        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, service_name, text);
 }
 
 static void
@@ -790,11 +817,12 @@ on_greeter_start_conversation (GdmGreeterServer *greeter_server,
 
 static void
 on_greeter_begin_verification (GdmGreeterServer *greeter_server,
+                               const char       *service_name,
                                GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: begin verification");
         gdm_session_setup (GDM_SESSION (slave->priv->session),
-                           "gdm");
+                           service_name);
 }
 
 static void
@@ -810,21 +838,23 @@ on_greeter_begin_auto_login (GdmGreeterServer *greeter_server,
 
 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");
         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);
+        gdm_session_answer_query (GDM_SESSION (slave->priv->session), service_name, text);
 }
 
 static void
@@ -887,18 +917,20 @@ on_greeter_connected (GdmGreeterServer *greeter_server,
 
 static void
 on_start_session_when_ready (GdmGreeterServer *session,
+                             const char       *service_name,
                              GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: Will start session when ready");
         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");
diff --git a/daemon/test-session.c b/daemon/test-session.c
index ed13ff7..7f6c5fe 100644
--- a/daemon/test-session.c
+++ b/daemon/test-session.c
@@ -44,10 +44,11 @@ on_conversation_started (GdmSession *session,
 
 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
@@ -79,10 +80,11 @@ on_session_reset_failed (GdmSession *session,
 
 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
@@ -97,14 +99,16 @@ on_session_authentication_failed (GdmSession *session,
 
 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)
 {
@@ -115,6 +119,7 @@ on_session_authorization_failed (GdmSession *session,
 
 static void
 on_session_accredited (GdmSession *session,
+                       const char *service_name,
                        gpointer    data)
 {
         char *username;
@@ -125,12 +130,13 @@ on_session_accredited (GdmSession *session,
                  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)
 {
@@ -165,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];
@@ -178,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);
@@ -191,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);
@@ -198,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];
@@ -222,7 +232,7 @@ on_secret_info_query (GdmSession *session,
 
         g_print ("\n");
 
-        gdm_session_answer_query (session, answer);
+        gdm_session_answer_query (session, service_name, answer);
 }
 
 static void
diff --git a/gui/simple-greeter/gdm-greeter-client.c b/gui/simple-greeter/gdm-greeter-client.c
index 8d061a4..0891e8b 100644
--- a/gui/simple-greeter/gdm-greeter-client.c
+++ b/gui/simple-greeter/gdm-greeter-client.c
@@ -134,6 +134,37 @@ emit_string_and_int_signal_for_message (GdmGreeterClient *client,
 }
 
 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,37 +231,35 @@ 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
@@ -307,14 +336,22 @@ send_dbus_string_method (DBusConnection *connection,
 }
 
 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,
@@ -328,8 +365,77 @@ send_dbus_bool_method (DBusConnection *connection,
 
         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,
@@ -412,37 +518,44 @@ gdm_greeter_client_call_begin_auto_login (GdmGreeterClient *client,
 }
 
 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
@@ -835,10 +948,10 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               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",
@@ -847,10 +960,10 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               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",
@@ -859,10 +972,10 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               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",
@@ -871,10 +984,10 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               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",
@@ -956,8 +1069,9 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               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 --git a/gui/simple-greeter/gdm-greeter-client.h b/gui/simple-greeter/gdm-greeter-client.h
index 88b0281..2f857dc 100644
--- a/gui/simple-greeter/gdm-greeter-client.h
+++ b/gui/simple-greeter/gdm-greeter-client.h
@@ -45,17 +45,22 @@ 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 (* reset)                   (GdmGreeterClient  *client);
         void (* selected_user_changed)   (GdmGreeterClient  *client,
                                           const char        *username);
@@ -69,7 +74,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 ())
@@ -95,8 +101,10 @@ void               gdm_greeter_client_call_start_conversation        (GdmGreeter
                                                                       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);
@@ -111,9 +119,11 @@ void               gdm_greeter_client_call_select_layout             (GdmGreeter
 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 --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index cd0cbdf..e856dd4 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -65,6 +65,7 @@ static gpointer session_object = NULL;
 
 static void
 on_info (GdmGreeterClient  *client,
+         const char        *service_name,
          const char        *text,
          GdmGreeterSession *session)
 {
@@ -75,6 +76,7 @@ on_info (GdmGreeterClient  *client,
 
 static void
 on_problem (GdmGreeterClient  *client,
+            const char        *service_name,
             const char        *text,
             GdmGreeterSession *session)
 {
@@ -173,6 +175,7 @@ on_user_authorized (GdmGreeterClient  *client,
 
 static void
 on_info_query (GdmGreeterClient  *client,
+               const char        *service_name,
                const char        *text,
                GdmGreeterSession *session)
 {
@@ -183,6 +186,7 @@ on_info_query (GdmGreeterClient  *client,
 
 static void
 on_secret_info_query (GdmGreeterClient  *client,
+                      const char        *service_name,
                       const char        *text,
                       GdmGreeterSession *session)
 {
@@ -204,7 +208,8 @@ static void
 on_begin_verification (GdmGreeterLoginWindow *login_window,
                        GdmGreeterSession     *session)
 {
-        gdm_greeter_client_call_begin_verification (session->priv->client);
+        gdm_greeter_client_call_begin_verification (session->priv->client,
+                                                    "gdm");
 }
 
 static void
@@ -213,6 +218,7 @@ on_begin_verification_for_user (GdmGreeterLoginWindow *login_window,
                                 GdmGreeterSession     *session)
 {
         gdm_greeter_client_call_begin_verification_for_user (session->priv->client,
+                                                             "gdm",
                                                              username);
 }
 
@@ -222,6 +228,7 @@ on_query_answer (GdmGreeterLoginWindow *login_window,
                  GdmGreeterSession     *session)
 {
         gdm_greeter_client_call_answer_query (session->priv->client,
+                                              "gdm",
                                               text);
 }
 
@@ -285,7 +292,7 @@ static void
 on_start_session (GdmGreeterLoginWindow *login_window,
                   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, "gdm", TRUE);
 }
 
 static int
-- 
1.7.0.1


From 9d25310a9d6f77c149357e0f6b0d18b6e9bf4b18 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Sat, 7 Feb 2009 11:36:40 -0500
Subject: [PATCH 07/47] emit "ConversationStopped" signal at end of conv

This will allow us to track when individual
PAM conversations fail, instead of doing one
giant reset.  The reason this is useful is that
some PAM modules fail immediately for some users
(for instance the fingerprint PAM module fails if
 a user hasn't enrolled their print).
---
 daemon/gdm-greeter-server.c  |   14 ++++++++++++++
 daemon/gdm-greeter-server.h  |    2 ++
 daemon/gdm-session-direct.c  |   36 ++++++++++++++++++++++++++++++++++++
 daemon/gdm-session-private.h |    2 ++
 daemon/gdm-session.c         |   28 ++++++++++++++++++++++++++++
 daemon/gdm-session.h         |    6 ++++++
 daemon/gdm-simple-slave.c    |   34 ++++++++++++++++++++++++++--------
 7 files changed, 114 insertions(+), 8 deletions(-)

diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index dae34c5..ecb2ad6 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -299,6 +299,14 @@ gdm_greeter_server_ready (GdmGreeterServer *greeter_server,
         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;
+}
+
 void
 gdm_greeter_server_selected_user_changed (GdmGreeterServer *greeter_server,
                                           const char       *username)
@@ -797,6 +805,9 @@ do_introspect (DBusConnection *connection,
                                "    <method name=\"StartConversation\">\n"
                                "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
+                               "    <method name=\"StopConversation\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
                                "    <method name=\"BeginVerification\">\n"
                                "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
@@ -868,6 +879,9 @@ do_introspect (DBusConnection *connection,
                                "    <signal name=\"Ready\">\n"
                                "      <arg name=\"service-name\" type=\"s\"/>\n"
                                "    </signal>\n"
+                               "    <signal name=\"ConversationStopped\">\n"
+                               "      <arg name=\"service-name\" type=\"s\"/>\n"
+                               "    </signal>\n"
                                "    <signal name=\"Reset\">\n"
                                "    </signal>\n"
                                "    <signal name=\"UserAuthorized\">\n"
diff --git a/daemon/gdm-greeter-server.h b/daemon/gdm-greeter-server.h
index 6d0dd87..976f0b7 100644
--- a/daemon/gdm-greeter-server.h
+++ b/daemon/gdm-greeter-server.h
@@ -96,6 +96,8 @@ gboolean            gdm_greeter_server_problem               (GdmGreeterServer *
 gboolean            gdm_greeter_server_reset                 (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,
diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 168bf73..09d6a76 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -1794,9 +1794,15 @@ worker_exited (GdmSessionWorkerJob *job,
 {
         g_debug ("GdmSessionDirect: Worker job exited: %d", code);
 
+        g_object_ref (conversation->job);
         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->job);
 }
 
 static void
@@ -1806,9 +1812,15 @@ worker_died (GdmSessionWorkerJob *job,
 {
         g_debug ("GdmSessionDirect: Worker job died: %d", signum);
 
+        g_object_ref (conversation->job);
         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->job);
 }
 
 static GdmSessionConversation *
@@ -1881,6 +1893,10 @@ stop_conversation (GdmSessionConversation *conversation)
 
         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
@@ -1901,6 +1917,25 @@ gdm_session_direct_start_conversation (GdmSession *session,
 }
 
 static void
+gdm_session_direct_stop_conversation (GdmSession *session,
+                                      const char *service_name)
+{
+        GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
+
+        g_return_if_fail (session != NULL);
+
+        g_debug ("GdmSessionDirect: stopping conversation");
+
+        conversation = find_conversation_by_name (impl, service_name);
+
+        if (conversation != NULL) {
+                stop_conversation (conversation);
+                g_hash_table_remove (impl->priv->conversations, service_name);
+        }
+}
+
+static void
 send_setup (GdmSessionDirect *session,
             const char       *service_name)
 {
@@ -2789,6 +2824,7 @@ static void
 gdm_session_iface_init (GdmSessionIface *iface)
 {
         iface->start_conversation = gdm_session_direct_start_conversation;
+        iface->stop_conversation = gdm_session_direct_stop_conversation;
         iface->setup = gdm_session_direct_setup;
         iface->setup_for_user = gdm_session_direct_setup_for_user;
         iface->authenticate = gdm_session_direct_authenticate;
diff --git a/daemon/gdm-session-private.h b/daemon/gdm-session-private.h
index 4dc6e44..36781dd 100644
--- a/daemon/gdm-session-private.h
+++ b/daemon/gdm-session-private.h
@@ -29,6 +29,8 @@ G_BEGIN_DECLS
 /* state changes */
 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,
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index a7b4e8b..8858071 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -30,6 +30,7 @@
 
 enum {
         CONVERSATION_STARTED = 0,
+        CONVERSATION_STOPPED,
         SETUP_COMPLETE,
         SETUP_FAILED,
         RESET_COMPLETE,
@@ -90,6 +91,15 @@ gdm_session_start_conversation (GdmSession *session,
 }
 
 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
 gdm_session_close (GdmSession *session)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
@@ -231,6 +241,16 @@ gdm_session_class_init (gpointer g_iface)
                               g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
                               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,
@@ -688,6 +708,14 @@ _gdm_session_conversation_started (GdmSession   *session,
 }
 
 void
+_gdm_session_conversation_stopped (GdmSession   *session,
+                                   const char   *service_name)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+        g_signal_emit (session, signals [CONVERSATION_STOPPED], 0, service_name);
+}
+
+void
 _gdm_session_closed (GdmSession   *session)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index 20a442b..22c2ccb 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -47,6 +47,8 @@ struct _GdmSessionIface
         /* Methods */
         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,
@@ -132,6 +134,8 @@ struct _GdmSessionIface
                                               int           signal_number);
         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);
@@ -148,6 +152,8 @@ GType    gdm_session_get_type                    (void) G_GNUC_CONST;
 
 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,
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index e2a7877..52e24fb 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -230,8 +230,7 @@ on_session_setup_failed (GdmSession     *session,
                                             message != NULL ? message:  _("Unable to initialize login system"));
         }
 
-        destroy_session (slave);
-        queue_greeter_reset (slave);
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static void
@@ -268,8 +267,8 @@ on_session_authentication_failed (GdmSession     *session,
                                             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
@@ -327,8 +326,7 @@ on_session_authorization_failed (GdmSession     *session,
                                             message != NULL ? message :  _("Unable to authorize user"));
         }
 
-        destroy_session (slave);
-        queue_greeter_reset (slave);
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static gboolean
@@ -471,9 +469,8 @@ on_session_accreditation_failed (GdmSession     *session,
            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
@@ -582,6 +579,23 @@ on_session_conversation_started (GdmSession     *session,
 }
 
 static void
+on_session_conversation_stopped (GdmSession     *session,
+                                 const char     *service_name,
+                                 GdmSimpleSlave *slave)
+{
+        gboolean res;
+        g_debug ("GdmSimpleSlave: conversation stopped");
+
+        if (slave->priv->greeter_server != NULL) {
+                res = gdm_greeter_server_conversation_stopped (slave->priv->greeter_server,
+                                                               service_name);
+                if (! res) {
+                        g_warning ("Unable to send conversation stopped");
+                }
+        }
+}
+
+static void
 on_session_selected_user_changed (GdmSession     *session,
                                   const char     *text,
                                   GdmSimpleSlave *slave)
@@ -670,6 +684,10 @@ create_new_session (GdmSimpleSlave *slave)
                           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",
                           G_CALLBACK (on_session_setup_complete),
                           slave);
-- 
1.7.0.1


From addb34cb8d5aec8dda0d463e38db941a4db16eca Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 30 Jan 2009 23:57:31 -0500
Subject: [PATCH 08/47] Add limited support for multiple pam stacks

This hard codes 3 pam stacks and doesn't handle
switching between them very well yet.
---
 gui/simple-greeter/Makefile.am                |    4 +
 gui/simple-greeter/gdm-greeter-login-window.c |   91 ++++++++++--
 gui/simple-greeter/gdm-greeter-login-window.h |   11 ++-
 gui/simple-greeter/gdm-greeter-session.c      |   30 +++-
 gui/simple-greeter/gdm-task-list.c            |  198 +++++++++++++++++++++++++
 gui/simple-greeter/gdm-task-list.h            |   64 ++++++++
 6 files changed, 378 insertions(+), 20 deletions(-)
 create mode 100644 gui/simple-greeter/gdm-task-list.c
 create mode 100644 gui/simple-greeter/gdm-task-list.h

diff --git a/gui/simple-greeter/Makefile.am b/gui/simple-greeter/Makefile.am
index ee06ea0..1ad22b4 100644
--- a/gui/simple-greeter/Makefile.am
+++ b/gui/simple-greeter/Makefile.am
@@ -85,6 +85,8 @@ 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			\
 	$(NULL)
 
 test_greeter_login_window_LDADD =	\
@@ -318,6 +320,8 @@ gdm_simple_greeter_SOURCES =  		\
 	gdm-session-option-widget.c	\
 	gdm-user-chooser-widget.h	\
 	gdm-user-chooser-widget.c	\
+	gdm-task-list.h			\
+	gdm-task-list.c			\
 	$(NULL)
 
 gdm_simple_greeter_LDADD = 		\
diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 25af747..36ba360 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -50,12 +50,16 @@
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
+#include "gdm-marshal.h"
+
 #include "gdm-settings-client.h"
 #include "gdm-settings-keys.h"
 #include "gdm-profile.h"
 
+#include "gdm-greeter-client.h"
 #include "gdm-greeter-login-window.h"
 #include "gdm-user-chooser-widget.h"
+#include "gdm-task-list.h"
 
 #ifdef HAVE_PAM
 #include <security/pam_appl.h>
@@ -98,6 +102,7 @@ struct GdmGreeterLoginWindowPrivate
 {
         GtkBuilder      *builder;
         GtkWidget       *user_chooser;
+        GtkWidget       *conversation_list;
         GtkWidget       *auth_banner_label;
         guint            display_is_local : 1;
         guint            is_interactive : 1;
@@ -125,6 +130,7 @@ enum {
 };
 
 enum {
+        START_CONVERSATION,
         BEGIN_AUTO_LOGIN,
         BEGIN_VERIFICATION,
         BEGIN_VERIFICATION_FOR_USER,
@@ -354,7 +360,7 @@ on_login_button_clicked_answer_query (GtkButton             *button,
         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);
+        g_signal_emit (login_window, signals[QUERY_ANSWER], 0, "gdm", text);
 }
 
 static void
@@ -599,26 +605,32 @@ gdm_greeter_login_window_reset (GdmGreeterLoginWindow *login_window)
 
 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);
 
         g_debug ("GdmGreeterLoginWindow: info: %s", text);
 
-        set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        if (strcmp (service_name, gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list))) == 0) {
+                set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        }
 
         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);
 
         g_debug ("GdmGreeterLoginWindow: problem: %s", text);
 
-        set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        if (strcmp (service_name, gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list))) == 0) {
+                set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        }
         gdk_window_beep (GTK_WIDGET (login_window)->window);
 
         return TRUE;
@@ -694,6 +706,7 @@ gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_
 
 gboolean
 gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
+                                     const char            *service_name,
                                      const char            *text)
 {
         GtkWidget  *entry;
@@ -701,6 +714,10 @@ gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
 
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
+        if (strcmp (service_name, gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list))) != 0) {
+                return TRUE;
+        }
+
         g_debug ("GdmGreeterLoginWindow: info query: %s", text);
 
         entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
@@ -723,6 +740,7 @@ gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
 
 gboolean
 gdm_greeter_login_window_secret_info_query (GdmGreeterLoginWindow *login_window,
+                                            const char            *service_name,
                                             const char            *text)
 {
         GtkWidget  *entry;
@@ -855,7 +873,10 @@ on_user_chosen (GdmUserChooserWidget  *user_chooser,
                        0, user_name);
 
         if (strcmp (user_name, GDM_USER_CHOOSER_USER_OTHER) == 0) {
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
+                const char *service_name;
+
+                service_name = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, service_name);
         } 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) {
@@ -869,7 +890,10 @@ on_user_chosen (GdmUserChooserWidget  *user_chooser,
                 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);
+                const char *service_name;
+
+                service_name = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, service_name, user_name);
         }
 
         switch_mode (login_window, MODE_AUTHENTICATION);
@@ -1032,6 +1056,20 @@ create_computer_info (GdmGreeterLoginWindow *login_window)
 #define INVISIBLE_CHAR_BULLET        0x2022
 #define INVISIBLE_CHAR_NONE          0
 
+static void
+on_task_activated (GdmGreeterLoginWindow *login_window,
+                   const char            *name)
+{
+        g_debug ("GdmGreeterLoginWindow: starting conversation with '%s'", name);
+        g_signal_emit (login_window, signals[START_CONVERSATION], 0, name);
+}
+
+static void
+on_task_deactivated (GdmGreeterLoginWindow *login_window,
+                     const char            *name)
+{
+        g_debug ("GdmGreeterLoginWindow: conversation '%s' now in background", name);
+}
 
 static void
 load_theme (GdmGreeterLoginWindow *login_window)
@@ -1106,6 +1144,28 @@ load_theme (GdmGreeterLoginWindow *login_window)
 
         gtk_widget_show (login_window->priv->user_chooser);
 
+        /* FIXME: task list should implement GtkBuildable and this should get dropped
+         */
+        login_window->priv->conversation_list = gdm_task_list_new ();
+        gtk_box_pack_start (GTK_BOX (box), login_window->priv->conversation_list, TRUE, TRUE, 0);
+
+        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);
+        gtk_widget_show (login_window->priv->conversation_list);
+
+        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                "password-auth", "dialog-password");
+        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                "fingerprint-auth", "stock_allow-effects");
+        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                "smartcard-auth", "badge-small");
+
         login_window->priv->auth_banner_label = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-banner-label"));
         /*make_label_small_italic (login_window->priv->auth_banner_label);*/
 
@@ -1276,6 +1336,15 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
         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),
@@ -1292,9 +1361,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
                               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),
@@ -1302,9 +1371,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
                               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),
@@ -1312,9 +1381,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
                               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),
diff --git a/gui/simple-greeter/gdm-greeter-login-window.h b/gui/simple-greeter/gdm-greeter-login-window.h
index 817d0a2..559b26b 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.h
+++ b/gui/simple-greeter/gdm-greeter-login-window.h
@@ -46,12 +46,17 @@ 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);
@@ -68,12 +73,16 @@ GtkWidget *         gdm_greeter_login_window_new                (gboolean displa
 gboolean            gdm_greeter_login_window_reset              (GdmGreeterLoginWindow *login_window);
 gboolean            gdm_greeter_login_window_ready              (GdmGreeterLoginWindow *login_window);
 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,
diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index e856dd4..945e210 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -71,7 +71,7 @@ on_info (GdmGreeterClient  *client,
 {
         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
@@ -82,7 +82,7 @@ on_problem (GdmGreeterClient  *client,
 {
         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
@@ -181,7 +181,7 @@ on_info_query (GdmGreeterClient  *client,
 {
         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
@@ -192,10 +192,18 @@ on_secret_info_query (GdmGreeterClient  *client,
 {
         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)
@@ -206,29 +214,32 @@ on_begin_auto_login (GdmGreeterLoginWindow *login_window,
 
 static void
 on_begin_verification (GdmGreeterLoginWindow *login_window,
+                       const char            *service_name,
                        GdmGreeterSession     *session)
 {
         gdm_greeter_client_call_begin_verification (session->priv->client,
-                                                    "gdm");
+                                                    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,
-                                                             "gdm",
+                                                             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,
-                                              "gdm",
+                                              service_name,
                                               text);
 }
 
@@ -392,7 +403,10 @@ toggle_login_window (GdmGreeterSession *session,
                 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),
diff --git a/gui/simple-greeter/gdm-task-list.c b/gui/simple-greeter/gdm-task-list.c
new file mode 100644
index 0000000..e0fd3d4
--- /dev/null
+++ b/gui/simple-greeter/gdm-task-list.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-task-list.h"
+
+#define GDM_TASK_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_TASK_LIST, GdmTaskListPrivate))
+
+typedef struct
+{
+        GtkWidget *radio_button;
+        char      *name;
+} GdmTask;
+
+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 *radio_button)
+{
+        GdmTask *task;
+
+        task = g_object_get_data (G_OBJECT (radio_button), "gdm-task");
+
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_button))) {
+                g_signal_emit (widget, signals[ACTIVATED], 0, task->name);
+        } else {
+                g_signal_emit (widget, signals[DEACTIVATED], 0, task->name);
+        }
+}
+
+void
+gdm_task_list_add_task (GdmTaskList *task_list,
+                        const char  *name,
+                        const char  *icon_name)
+{
+        GdmTask   *task;
+        GtkWidget *image;
+
+        task = g_new0 (GdmTask, 1);
+
+        task->name = g_strdup (name);
+        if (task_list->priv->tasks == NULL) {
+                task->radio_button = gtk_radio_button_new (NULL);
+        } else {
+                task->radio_button = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (((GdmTask *) task_list->priv->tasks->data)->radio_button));
+        }
+
+        g_object_set (task->radio_button, "draw-indicator", FALSE, NULL);
+        g_object_set_data (G_OBJECT (task->radio_button), "gdm-task", task);
+        g_signal_connect_swapped (task->radio_button,
+                                  "toggled", G_CALLBACK (on_task_toggled), task_list);
+        image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DND);
+        gtk_widget_show (image);
+        gtk_container_add (GTK_CONTAINER (task->radio_button), image);
+        gtk_widget_show (task->radio_button);
+        gtk_container_add (GTK_CONTAINER (task->radio_button), task_list->priv->box);
+
+        gtk_container_add (GTK_CONTAINER (task_list->priv->box), task->radio_button);
+        task_list->priv->tasks = g_list_append (task_list->priv->tasks, 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__STRING,
+                                            G_TYPE_NONE,
+                                            1, G_TYPE_STRING);
+
+        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__STRING,
+                                            G_TYPE_NONE,
+                                            1, G_TYPE_STRING);
+
+        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 (FALSE, 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);
+}
+
+const char *
+gdm_task_list_get_active_task (GdmTaskList *widget)
+{
+        GList *node;
+
+        for (node = widget->priv->tasks; node != NULL; node = node->next) {
+                GdmTask *task;
+
+                task = node->data;
+
+                if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (task->radio_button)) ) {
+                        return task->name;
+                }
+        }
+
+        return NULL;
+}
diff --git a/gui/simple-greeter/gdm-task-list.h b/gui/simple-greeter/gdm-task-list.h
new file mode 100644
index 0000000..ade21b6
--- /dev/null
+++ b/gui/simple-greeter/gdm-task-list.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_TASK_LIST_H
+#define __GDM_TASK_LIST_H
+
+#include <glib-object.h>
+#include <gtk/gtkalignment.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
+{
+        GtkAlignment             parent;
+        GdmTaskListPrivate *priv;
+} GdmTaskList;
+
+typedef struct
+{
+        GtkAlignmentClass       parent_class;
+
+        void (* deactivated)      (GdmTaskList *widget,
+                                   const char  *name);
+        void (* activated)      (GdmTaskList *widget,
+                                 const char  *name);
+} GdmTaskListClass;
+
+
+GType                  gdm_task_list_get_type               (void);
+GtkWidget *            gdm_task_list_new                    (void);
+
+const char *           gdm_task_list_get_active_task (GdmTaskList *widget);
+void                   gdm_task_list_add_task (GdmTaskList *widget,
+                                               const char  *name,
+                                               const char  *icon_name);
+G_END_DECLS
+
+#endif /* __GDM_TASK_LIST_H */
-- 
1.7.0.1


From b3704382fc973cb1222d91c487da283f93576b98 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 6 Mar 2009 11:19:40 -0500
Subject: [PATCH 09/47] Create session settings object when first starting worker

This is because one PAM module may complete before setup
gets called on another, and when one completes *all* PAM
modules get told about language, session, and layouts
set by the user.
---
 daemon/gdm-session-worker.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 2f07e13..8328910 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -2316,8 +2316,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),
@@ -2970,6 +2968,7 @@ gdm_session_worker_init (GdmSessionWorker *worker)
                                                            g_str_equal,
                                                            (GDestroyNotify) g_free,
                                                            (GDestroyNotify) g_free);
+        worker->priv->user_settings = gdm_session_settings_new ();
 }
 
 static void
-- 
1.7.0.1


From 57679898d909be56f6312091e7f1f53cdd1076c9 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 5 Feb 2009 15:20:25 -0500
Subject: [PATCH 10/47] Queue a greeter reset when the user clicks cancel

---
 daemon/gdm-simple-slave.c |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 52e24fb..86aa706 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -829,6 +829,9 @@ on_greeter_start_conversation (GdmGreeterServer *greeter_server,
                                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);
 }
@@ -839,6 +842,9 @@ on_greeter_begin_verification (GdmGreeterServer *greeter_server,
                                GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: begin verification");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_setup (GDM_SESSION (slave->priv->session),
                            service_name);
 }
@@ -849,6 +855,9 @@ on_greeter_begin_auto_login (GdmGreeterServer *greeter_server,
                              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);
@@ -861,6 +870,9 @@ on_greeter_begin_verification_for_user (GdmGreeterServer *greeter_server,
                                         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),
                                     service_name,
                                     username);
@@ -872,6 +884,9 @@ on_greeter_answer (GdmGreeterServer *greeter_server,
                    const char       *text,
                    GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_answer_query (GDM_SESSION (slave->priv->session), service_name, text);
 }
 
@@ -880,6 +895,9 @@ on_greeter_session_selected (GdmGreeterServer *greeter_server,
                              const char       *text,
                              GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_select_session (GDM_SESSION (slave->priv->session), text);
 }
 
@@ -888,6 +906,9 @@ on_greeter_language_selected (GdmGreeterServer *greeter_server,
                               const char       *text,
                               GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_select_language (GDM_SESSION (slave->priv->session), text);
 }
 
@@ -896,6 +917,9 @@ on_greeter_layout_selected (GdmGreeterServer *greeter_server,
                             const char       *text,
                             GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_select_layout (GDM_SESSION (slave->priv->session), text);
 }
 
@@ -912,7 +936,11 @@ on_greeter_cancel (GdmGreeterServer *greeter_server,
                    GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: Greeter cancelled");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         reset_session (slave);
+        queue_greeter_reset (slave);
 }
 
 static void
@@ -922,6 +950,9 @@ on_greeter_connected (GdmGreeterServer *greeter_server,
         gboolean display_is_local;
 
         g_debug ("GdmSimpleSlave: Greeter connected");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
 
         g_object_get (slave,
                       "display-is-local", &display_is_local,
@@ -939,6 +970,9 @@ on_start_session_when_ready (GdmGreeterServer *session,
                              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) {
@@ -952,6 +986,9 @@ on_start_session_later (GdmGreeterServer *session,
                         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;
 }
 
-- 
1.7.0.1


From 07cf93e20ac862617e44d684ff7d0ce174ebe5fd Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 6 Feb 2009 16:23:48 -0500
Subject: [PATCH 11/47] Add a plugin based extension system to greeter

This allows plugins to drive which PAM conversations
get run.  This commit just adds one plugin "password"
which does the one PAM conversation we've traditionally
run.
---
 configure.ac                                       |   44 ++
 gui/simple-greeter/Makefile.am                     |   15 +
 gui/simple-greeter/gdm-greeter-client.c            |   21 +
 gui/simple-greeter/gdm-greeter-client.h            |    2 +
 gui/simple-greeter/gdm-greeter-login-window.c      |  674 ++++++++++++++++----
 gui/simple-greeter/gdm-greeter-login-window.h      |   21 +-
 gui/simple-greeter/gdm-greeter-login-window.ui     |   42 +--
 gui/simple-greeter/gdm-greeter-plugin.c            |  255 ++++++++
 gui/simple-greeter/gdm-greeter-plugin.h            |   61 ++
 gui/simple-greeter/gdm-greeter-session.c           |   91 +++-
 gui/simple-greeter/gdm-plugin-manager.c            |  478 ++++++++++++++
 gui/simple-greeter/gdm-plugin-manager.h            |   66 ++
 gui/simple-greeter/gdm-task-list.c                 |  223 +++++--
 gui/simple-greeter/gdm-task-list.h                 |   36 +-
 gui/simple-greeter/libgdmsimplegreeter/Makefile.am |   46 ++
 .../libgdmsimplegreeter/gdm-conversation.c         |  147 +++++
 .../libgdmsimplegreeter/gdm-conversation.h         |   87 +++
 .../libgdmsimplegreeter/gdm-greeter-extension.c    |   93 +++
 .../libgdmsimplegreeter/gdm-greeter-extension.h    |   55 ++
 gui/simple-greeter/libgdmsimplegreeter/gdm-task.c  |  117 ++++
 gui/simple-greeter/libgdmsimplegreeter/gdm-task.h  |   62 ++
 .../libgdmsimplegreeter/gdmsimplegreeter.pc.in     |   11 +
 gui/simple-greeter/plugins/Makefile.am             |    1 +
 gui/simple-greeter/plugins/password/Makefile.am    |   53 ++
 .../plugins/password/gdm-password-extension.c      |  314 +++++++++
 .../plugins/password/gdm-password-extension.h      |   56 ++
 .../plugins/password/gdm-password.pam              |   19 +
 gui/simple-greeter/plugins/password/page.ui        |   56 ++
 gui/simple-greeter/plugins/password/plugin.c       |   40 ++
 po/POTFILES.in                                     |    1 +
 30 files changed, 2960 insertions(+), 227 deletions(-)
 create mode 100644 gui/simple-greeter/gdm-greeter-plugin.c
 create mode 100644 gui/simple-greeter/gdm-greeter-plugin.h
 create mode 100644 gui/simple-greeter/gdm-plugin-manager.c
 create mode 100644 gui/simple-greeter/gdm-plugin-manager.h
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/Makefile.am
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
 create mode 100644 gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
 create mode 100644 gui/simple-greeter/plugins/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/password/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/password/gdm-password-extension.c
 create mode 100644 gui/simple-greeter/plugins/password/gdm-password-extension.h
 create mode 100644 gui/simple-greeter/plugins/password/gdm-password.pam
 create mode 100644 gui/simple-greeter/plugins/password/page.ui
 create mode 100644 gui/simple-greeter/plugins/password/plugin.c

diff --git a/configure.ac b/configure.ac
index a692cca..e75bc93 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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)
@@ -193,6 +209,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 ---------------------------------------------------------------------------
 
@@ -1263,6 +1288,21 @@ fi
 
 AC_SUBST(GDM_SCREENSHOT_DIR)
 
+dnl ---------------------------------------------------------------------------
+dnl - Directory for simple greeter plugins
+dnl ---------------------------------------------------------------------------
+
+AC_ARG_WITH(simple-greeter-plugins-dir,
+            AS_HELP_STRING([--with-simple-greeter-plugins-dir=<dir>],
+                           [simple greeter plugins directory]))
+
+if ! test -z "$with_simple_greeter_plugins_dir"; then
+   GDM_SIMPLE_GREETER_PLUGINS_DIR=$with_simple_greeter_plugins_dir
+else
+   GDM_SIMPLE_GREETER_PLUGINS_DIR=${libdir}/gdm/simple-greeter/plugins
+fi
+
+AC_SUBST(GDM_SIMPLE_GREETER_PLUGINS_DIR)
 
 dnl ---------------------------------------------------------------------------
 dnl - Finish
@@ -1391,6 +1431,10 @@ 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-chooser/Makefile
 gui/user-switch-applet/Makefile
 utils/Makefile
diff --git a/gui/simple-greeter/Makefile.am b/gui/simple-greeter/Makefile.am
index 1ad22b4..636fc77 100644
--- a/gui/simple-greeter/Makefile.am
+++ b/gui/simple-greeter/Makefile.am
@@ -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)"\"		 	\
@@ -20,6 +23,7 @@ AM_CPPFLAGS = \
 	-DAT_SPI_REGISTRYD_DIR="\"$(AT_SPI_REGISTRYD_DIR)\""	\
 	$(DEVKIT_POWER_CFLAGS)				\
 	-DI_KNOW_THE_DEVICEKIT_POWER_API_IS_SUBJECT_TO_CHANGE	\
+	-DGDM_SIMPLE_GREETER_PLUGINS_DIR="\"$(GDM_SIMPLE_GREETER_PLUGINS_DIR)\""\
 	$(DISABLE_DEPRECATED_CFLAGS)			\
 	$(GTK_CFLAGS)					\
 	$(SIMPLE_GREETER_CFLAGS)			\
@@ -87,10 +91,15 @@ test_greeter_login_window_SOURCES = 	\
 	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)		\
@@ -141,6 +150,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)			\
@@ -314,10 +324,14 @@ 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			\
@@ -328,6 +342,7 @@ 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 --git a/gui/simple-greeter/gdm-greeter-client.c b/gui/simple-greeter/gdm-greeter-client.c
index 0891e8b..12572fe 100644
--- a/gui/simple-greeter/gdm-greeter-client.c
+++ b/gui/simple-greeter/gdm-greeter-client.c
@@ -64,6 +64,7 @@ enum {
         INFO_QUERY,
         SECRET_INFO_QUERY,
         READY,
+        CONVERSATION_STOPPED,
         RESET,
         SELECTED_USER_CHANGED,
         DEFAULT_LANGUAGE_NAME_CHANGED,
@@ -270,6 +271,13 @@ on_ready (GdmGreeterClient *client,
 }
 
 static void
+on_conversation_stopped (GdmGreeterClient *client,
+                         DBusMessage      *message)
+{
+        emit_string_signal_for_message (client, "ConversationStopped", message, CONVERSATION_STOPPED);
+}
+
+static void
 on_reset (GdmGreeterClient *client,
           DBusMessage      *message)
 {
@@ -760,6 +768,8 @@ client_dbus_handle_message (DBusConnection *connection,
                 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")) {
@@ -1000,6 +1010,17 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               G_TYPE_NONE,
                               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",
                               G_OBJECT_CLASS_TYPE (object_class),
diff --git a/gui/simple-greeter/gdm-greeter-client.h b/gui/simple-greeter/gdm-greeter-client.h
index 2f857dc..f879307 100644
--- a/gui/simple-greeter/gdm-greeter-client.h
+++ b/gui/simple-greeter/gdm-greeter-client.h
@@ -61,6 +61,8 @@ typedef struct
                                           const char        *problem);
         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);
diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 36ba360..a51184f 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008, 2009 Red Hat, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * Written by: William Jon McCann <mccann@jhu.edu>
+ *             Ray Strode <rstrode@redhat.com>
+ *
  */
 
 #include "config.h"
@@ -104,6 +107,7 @@ struct GdmGreeterLoginWindowPrivate
         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;
@@ -155,6 +159,8 @@ static void     on_user_unchosen            (GdmUserChooserWidget *user_chooser,
 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)
 
@@ -180,9 +186,6 @@ set_sensitive (GdmGreeterLoginWindow *login_window,
 {
         GtkWidget *box;
 
-        box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-input-box"));
-        gtk_widget_set_sensitive (box, sensitive);
-
         box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "buttonbox"));
         gtk_widget_set_sensitive (box, sensitive);
 
@@ -192,27 +195,43 @@ set_sensitive (GdmGreeterLoginWindow *login_window,
 static void
 set_focus (GdmGreeterLoginWindow *login_window)
 {
-        GtkWidget *entry;
-
-        entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "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 = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "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
@@ -347,30 +366,76 @@ sensitize_widget (GdmGreeterLoginWindow *login_window,
 }
 
 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 = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "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, "gdm", 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,
@@ -389,6 +454,7 @@ set_log_in_button_mode (GdmGreeterLoginWindow *login_window,
                         int                    mode)
 {
         GtkWidget *button;
+        GdmTask   *task;
 
         button = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "log-in-button"));
         gtk_widget_grab_default (button);
@@ -401,6 +467,12 @@ set_log_in_button_mode (GdmGreeterLoginWindow *login_window,
 
         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:
@@ -408,11 +480,18 @@ set_log_in_button_mode (GdmGreeterLoginWindow *login_window,
                 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:
@@ -458,8 +537,7 @@ switch_mode (GdmGreeterLoginWindow *login_window,
                 set_log_in_button_mode (login_window, LOGIN_BUTTON_HIDDEN);
 
                 show_widget (login_window, "cancel-button", FALSE);
-
-                show_widget (login_window, "auth-input-box", FALSE);
+                show_widget (login_window, "auth-page-box", FALSE);
 
                 sensitize_widget (login_window, "disconnect-button", FALSE);
 
@@ -468,6 +546,7 @@ switch_mode (GdmGreeterLoginWindow *login_window,
         case MODE_AUTHENTICATION:
                 show_widget (login_window, "cancel-button", TRUE);
                 show_widget (login_window, "disconnect-button", FALSE);
+                show_widget (login_window, "auth-page-box", TRUE);
                 default_name = "log-in-button";
                 break;
         default:
@@ -505,25 +584,40 @@ switch_mode (GdmGreeterLoginWindow *login_window,
         }
 }
 
-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;
+}
+
+static gboolean
+reset_task (GdmTaskList           *task_list,
+            GdmTask               *task,
+            GdmGreeterLoginWindow *login_window)
 {
-        const char *typed_text;
-        char       *null_text;
+        char *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), "");
+        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);
@@ -547,16 +641,13 @@ reset_dialog (GdmGreeterLoginWindow *login_window)
                 login_window->priv->start_session_handler_id = 0;
         }
 
-        entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "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 = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-label"));
-        gtk_label_set_text (GTK_LABEL (label), "");
-
         switch_mode (login_window, MODE_SELECTION);
 
         set_sensitive (login_window, TRUE);
@@ -580,11 +671,22 @@ do_cancel (GdmGreeterLoginWindow *login_window)
 }
 
 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 = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           (GdmTaskListForeachFunc)
+                                           task_has_service_name,
+                                           (gpointer) service_name);
+
+        if (task != NULL) {
+                gdm_conversation_set_ready (GDM_CONVERSATION (task));
+                g_object_unref (task);
+        }
 
         set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
         set_ready (GDM_GREETER_LOGIN_WINDOW (login_window));
@@ -594,12 +696,63 @@ gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window)
 }
 
 gboolean
-gdm_greeter_login_window_reset (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_window,
+                                               const char            *service_name)
 {
+        GdmTask *task;
+
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
+        g_debug ("GdmGreeterLoginWindow: conversation '%s' has stopped", service_name);
+
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           (GdmTaskListForeachFunc)
+                                           task_has_service_name,
+                                           (gpointer) service_name);
+
+        if (task != NULL) {
+                gdm_conversation_reset (GDM_CONVERSATION (task));
+                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;
 }
 
@@ -608,12 +761,20 @@ 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);
 
-        if (strcmp (service_name, gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list))) == 0) {
-                set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                        (GdmTaskListForeachFunc)
+                                        task_has_service_name,
+                                        (gpointer) service_name);
+
+        if (task != NULL) {
+                gdm_conversation_set_message (GDM_CONVERSATION (task),
+                                              text);
+                g_object_unref (task);
         }
 
         return TRUE;
@@ -624,13 +785,22 @@ 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);
 
-        if (strcmp (service_name, gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list))) == 0) {
-                set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                        (GdmTaskListForeachFunc)
+                                        task_has_service_name,
+                                        (gpointer) 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;
@@ -668,11 +838,21 @@ gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_windo
 }
 
 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 "
@@ -682,8 +862,8 @@ gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_
                  */
                 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
@@ -709,26 +889,24 @@ 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);
 
-        if (strcmp (service_name, gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list))) != 0) {
-                return TRUE;
-        }
-
         g_debug ("GdmGreeterLoginWindow: info query: %s", text);
 
-        entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "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);
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                        (GdmTaskListForeachFunc)
+                                        task_has_service_name,
+                                        (gpointer) service_name);
 
-        label = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-label"));
-        gtk_label_set_text (GTK_LABEL (label), text);
+        if (task != NULL) {
+                gdm_conversation_ask_question (GDM_CONVERSATION (task),
+                                               text);
+                g_object_unref (task);
+        }
 
-        show_widget (login_window, "auth-input-box", TRUE);
+        set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
         set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
         set_ready (GDM_GREETER_LOGIN_WINDOW (login_window));
         set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
@@ -743,20 +921,23 @@ 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 = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "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 = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                        (GdmTaskListForeachFunc)
+                                        task_has_service_name,
+                                        (gpointer) service_name);
 
-        label = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "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));
@@ -767,13 +948,16 @@ gdm_greeter_login_window_secret_info_query (GdmGreeterLoginWindow *login_window,
 }
 
 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
@@ -856,6 +1040,46 @@ on_users_loaded (GdmUserChooserWidget  *user_chooser,
         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 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));
+
+        if (user_name == NULL) {
+                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
 on_user_chosen (GdmUserChooserWidget  *user_chooser,
                 GdmGreeterLoginWindow *login_window)
@@ -873,10 +1097,10 @@ on_user_chosen (GdmUserChooserWidget  *user_chooser,
                        0, user_name);
 
         if (strcmp (user_name, GDM_USER_CHOOSER_USER_OTHER) == 0) {
-                const char *service_name;
-
-                service_name = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, service_name);
+                gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                            (GdmTaskListForeachFunc)
+                                            begin_task_verification,
+                                            login_window);
         } 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) {
@@ -890,10 +1114,10 @@ on_user_chosen (GdmUserChooserWidget  *user_chooser,
                 set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
                 set_message (login_window, _("Select language and click Log In"));
         } else {
-                const char *service_name;
-
-                service_name = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, service_name, user_name);
+                gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                            (GdmTaskListForeachFunc)
+                                            begin_task_verification_for_selected_user,
+                                            login_window);
         }
 
         switch_mode (login_window, MODE_AUTHENTICATION);
@@ -1058,23 +1282,70 @@ create_computer_info (GdmGreeterLoginWindow *login_window)
 
 static void
 on_task_activated (GdmGreeterLoginWindow *login_window,
-                   const char            *name)
+                   GdmTask               *task)
 {
-        g_debug ("GdmGreeterLoginWindow: starting conversation with '%s'", name);
-        g_signal_emit (login_window, signals[START_CONVERSATION], 0, name);
+        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,
-                     const char            *name)
+                     GdmTask               *task)
 {
-        g_debug ("GdmGreeterLoginWindow: conversation '%s' now in background", name);
+        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 void
 load_theme (GdmGreeterLoginWindow *login_window)
 {
-        GtkWidget *entry;
         GtkWidget *button;
         GtkWidget *box;
         GtkWidget *image;
@@ -1159,27 +1430,13 @@ load_theme (GdmGreeterLoginWindow *login_window)
                                   login_window);
         gtk_widget_show (login_window->priv->conversation_list);
 
-        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                "password-auth", "dialog-password");
-        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                "fingerprint-auth", "stock_allow-effects");
-        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                "smartcard-auth", "badge-small");
-
         login_window->priv->auth_banner_label = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-banner-label"));
         /*make_label_small_italic (login_window->priv->auth_banner_label);*/
+        login_window->priv->auth_page_box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-page-box"));
 
         button = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "cancel-button"));
         g_signal_connect (button, "clicked", G_CALLBACK (cancel_button_clicked), login_window);
 
-        entry = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "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 = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "computer-info-event-box"));
@@ -1421,9 +1678,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
                               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,
@@ -1476,6 +1733,187 @@ on_gconf_key_changed (GConfClient           *client,
         }
 }
 
+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);
+}
+
+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 = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "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);
+
+        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);
+
+        g_free (name);
+        g_free (description);
+
+        if (gdm_task_list_get_number_of_tasks (GDM_TASK_LIST (login_window->priv->conversation_list)) == 0) {
+                gtk_widget_hide (login_window->priv->conversation_list);
+        } else {
+                gtk_widget_show (login_window->priv->conversation_list);
+        }
+
+        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));
+        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 --git a/gui/simple-greeter/gdm-greeter-login-window.h b/gui/simple-greeter/gdm-greeter-login-window.h
index 559b26b..c312a47 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.h
+++ b/gui/simple-greeter/gdm-greeter-login-window.h
@@ -23,6 +23,9 @@
 #define __GDM_GREETER_LOGIN_WINDOW_H
 
 #include <glib-object.h>
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+#include "gdm-greeter-extension.h"
 
 G_BEGIN_DECLS
 
@@ -33,6 +36,8 @@ G_BEGIN_DECLS
 #define GDM_IS_GREETER_LOGIN_WINDOW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_GREETER_LOGIN_WINDOW))
 #define GDM_GREETER_LOGIN_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_GREETER_LOGIN_WINDOW, GdmGreeterLoginWindowClass))
 
+#define GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION(e) (GDM_IS_CONVERSATION(e) && GDM_IS_TASK(e))
+
 typedef struct GdmGreeterLoginWindowPrivate GdmGreeterLoginWindowPrivate;
 
 typedef struct
@@ -62,7 +67,8 @@ typedef struct
                                               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;
 
@@ -71,7 +77,10 @@ GtkWidget *         gdm_greeter_login_window_new                (gboolean displa
 
 
 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);
@@ -88,7 +97,13 @@ gboolean            gdm_greeter_login_window_problem            (GdmGreeterLogin
 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 --git a/gui/simple-greeter/gdm-greeter-login-window.ui b/gui/simple-greeter/gdm-greeter-login-window.ui
index 48c6762..4dbd825 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.ui
+++ b/gui/simple-greeter/gdm-greeter-login-window.ui
@@ -163,30 +163,8 @@
                           <placeholder/>
                         </child>
                         <child>
-                          <object class="GtkHBox" id="auth-input-box">
+                          <object class="GtkHBox" id="auth-page-box">
                             <property name="visible">True</property>
-                            <property name="spacing">6</property>
-                            <child>
-                              <object class="GtkLabel" id="auth-prompt-label">
-                                <property name="visible">True</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkEntry" id="auth-prompt-entry">
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                <property name="activates_default">True</property>
-                              </object>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
                             <child>
                               <placeholder/>
                             </child>
@@ -194,24 +172,6 @@
                           <packing>
                             <property name="expand">False</property>
                             <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkHBox" id="auth-message-box">
-                            <property name="visible">True</property>
-                            <child>
-                              <object class="GtkLabel" id="auth-message-label">
-                                <property name="visible">True</property>
-                              </object>
-                              <packing>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
                             <property name="position">2</property>
                           </packing>
                         </child>
diff --git a/gui/simple-greeter/gdm-greeter-plugin.c b/gui/simple-greeter/gdm-greeter-plugin.c
new file mode 100644
index 0000000..02814a2
--- /dev/null
+++ b/gui/simple-greeter/gdm-greeter-plugin.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "gdm-greeter-extension.h"
+#include "gdm-greeter-plugin.h"
+
+#define GDM_GREETER_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_PLUGIN, GdmGreeterPluginPrivate))
+
+enum {
+        PROP_0,
+        PROP_FILENAME,
+};
+
+enum {
+        LOADED,
+        LOAD_FAILED,
+        UNLOADED,
+        LAST_SIGNAL
+};
+
+struct _GdmGreeterPluginPrivate {
+        GObject              parent;
+
+        GModule             *module;
+        char                *filename;
+
+        GdmGreeterExtension *extension;
+};
+
+static void gdm_greeter_plugin_finalize     (GObject      *object);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GdmGreeterPlugin, gdm_greeter_plugin, G_TYPE_OBJECT)
+
+static void
+gdm_greeter_plugin_set_property (GObject      *object,
+                                 guint         param_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+        GdmGreeterPlugin *plugin;
+
+        plugin = GDM_GREETER_PLUGIN (object);
+        switch (param_id) {
+        case PROP_FILENAME:
+                plugin->priv->filename = g_strdup (g_value_get_string (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                break;
+        }
+}
+
+static void
+gdm_greeter_plugin_get_property (GObject    *object,
+                                 guint       param_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+        GdmGreeterPlugin *plugin;
+
+        plugin = GDM_GREETER_PLUGIN (object);
+
+        switch (param_id) {
+        case PROP_FILENAME:
+                g_value_set_string (value, plugin->priv->filename);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                break;
+        }
+}
+
+static void
+gdm_greeter_plugin_class_init (GdmGreeterPluginClass *class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (class);
+
+        gobject_class->set_property = gdm_greeter_plugin_set_property;
+        gobject_class->get_property = gdm_greeter_plugin_get_property;
+        gobject_class->finalize = gdm_greeter_plugin_finalize;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILENAME,
+                                         g_param_spec_string ("filename",
+                                                              "Filename",
+                                                              "The full path to the plugin.",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+        signals [LOADED] =
+                g_signal_new ("loaded",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPluginClass, loaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [LOAD_FAILED] =
+                g_signal_new ("load-failed",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPluginClass, load_failed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [UNLOADED] =
+                g_signal_new ("unloaded",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPluginClass, unloaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        g_type_class_add_private (class, sizeof (GdmGreeterPluginPrivate));
+}
+
+GdmGreeterPlugin *
+gdm_greeter_plugin_new (const char *filename)
+{
+        GObject *object;
+
+        object = g_object_new (GDM_TYPE_GREETER_PLUGIN,
+                               "filename", filename, NULL);
+
+        return GDM_GREETER_PLUGIN (object);
+}
+
+void
+gdm_greeter_plugin_load (GdmGreeterPlugin *plugin)
+{
+        GModule *module;
+        GdmGreeterExtension *extension;
+        union {
+                gpointer symbol;
+                GdmGreeterPluginGetExtensionFunc invoke;
+        } get_extension;
+
+
+        module = g_module_open (plugin->priv->filename, G_MODULE_BIND_LOCAL);
+
+        if (module == NULL) {
+                g_warning ("plugin %s couldn't be opened: %s",
+                           plugin->priv->filename,
+                           g_module_error ());
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+                return;
+        }
+
+        if (!g_module_symbol (module,
+                              "gdm_greeter_plugin_get_extension",
+                              &get_extension.symbol) ||
+            !get_extension.symbol) {
+                g_warning ("plugin %s lacks gdm_greeter_plugin_get_extension()",
+                           plugin->priv->filename);
+                g_module_close (module);
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+                return;
+        }
+
+        extension = get_extension.invoke ();
+
+        if (!extension) {
+                g_warning ("plugin %s didn't return extension when asked",
+                           plugin->priv->filename);
+                g_module_close (module);
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+        }
+
+        if (!GDM_IS_GREETER_EXTENSION (extension)) {
+                g_warning ("plugin %s returned bogus extension when asked",
+                           plugin->priv->filename);
+                g_module_close (module);
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+        }
+
+        plugin->priv->module = module;
+        plugin->priv->extension = extension;
+
+        g_signal_emit (plugin, signals [LOADED], 0);
+}
+
+void
+gdm_greeter_plugin_unload (GdmGreeterPlugin *plugin)
+{
+        if (plugin->priv->extension != NULL) {
+                g_object_unref (plugin->priv->extension);
+                plugin->priv->extension = NULL;
+        }
+
+        if (plugin->priv->module != NULL) {
+                g_module_close (plugin->priv->module);
+                plugin->priv->module = NULL;
+        }
+}
+
+const char *
+gdm_greeter_plugin_get_filename (GdmGreeterPlugin *plugin)
+{
+        return plugin->priv->filename;
+}
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (GdmGreeterPlugin *plugin)
+{
+        return g_object_ref (plugin->priv->extension);
+}
+
+static void
+gdm_greeter_plugin_init (GdmGreeterPlugin *plugin)
+{
+        plugin->priv = GDM_GREETER_PLUGIN_GET_PRIVATE (plugin);
+}
+
+static void
+gdm_greeter_plugin_finalize (GObject *object)
+{
+        GdmGreeterPlugin *plugin;
+
+        plugin = GDM_GREETER_PLUGIN (object);
+
+        gdm_greeter_plugin_unload (plugin);
+}
+
diff --git a/gui/simple-greeter/gdm-greeter-plugin.h b/gui/simple-greeter/gdm-greeter-plugin.h
new file mode 100644
index 0000000..904c231
--- /dev/null
+++ b/gui/simple-greeter/gdm-greeter-plugin.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __GDM_GREETER_PLUGIN
+#define __GDM_GREETER_PLUGIN
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_GREETER_PLUGIN (gdm_greeter_plugin_get_type ())
+#define GDM_GREETER_PLUGIN(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_GREETER_PLUGIN, GdmGreeterPlugin))
+#define GDM_IS_GREETER_PLUGIN(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_GREETER_PLUGIN))
+
+typedef struct _GdmGreeterPlugin GdmGreeterPlugin;
+typedef struct _GdmGreeterPluginPrivate GdmGreeterPluginPrivate;
+typedef struct _GdmGreeterPluginClass GdmGreeterPluginClass;
+
+struct _GdmGreeterPlugin
+{
+        GObject                parent;
+        GdmGreeterPluginPrivate *priv;
+};
+
+struct _GdmGreeterPluginClass
+{
+        GObjectClass   parent_class;
+
+        void          (* loaded)         (GdmGreeterPlugin *plugin);
+        void          (* load_failed)    (GdmGreeterPlugin *plugin);
+        void          (* unloaded)       (GdmGreeterPlugin *plugin);
+};
+
+GType             gdm_greeter_plugin_get_type (void) G_GNUC_CONST;
+GdmGreeterPlugin *gdm_greeter_plugin_new (const char *filename);
+void              gdm_greeter_plugin_load (GdmGreeterPlugin *plugin);
+void              gdm_greeter_plugin_unload (GdmGreeterPlugin *plugin);
+const char       *gdm_greeter_plugin_get_filename (GdmGreeterPlugin *plugin);
+GdmGreeterExtension *gdm_greeter_plugin_get_extension (GdmGreeterPlugin *plugin);
+
+G_END_DECLS
+
+#endif
diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index 945e210..848ea1e 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -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 {
@@ -92,7 +96,19 @@ on_ready (GdmGreeterClient  *client,
 {
         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
@@ -167,10 +183,11 @@ on_timed_login_requested (GdmGreeterClient  *client,
 
 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
@@ -289,7 +306,6 @@ on_cancelled (GdmGreeterLoginWindow *login_window,
 {
         gdm_greeter_panel_hide_user_options (GDM_GREETER_PANEL (session->priv->panel));
         gdm_greeter_client_call_cancel (session->priv->client);
-        gdm_greeter_client_call_start_conversation (session->priv->client, "gdm");
 }
 
 static void
@@ -301,9 +317,10 @@ on_disconnected (GdmGreeterLoginWindow *login_window,
 
 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, "gdm", TRUE);
+        gdm_greeter_client_call_start_session_when_ready (session->priv->client, service_name, TRUE);
 }
 
 static int
@@ -462,8 +479,6 @@ gdm_greeter_session_start (GdmGreeterSession *session,
         toggle_panel (session, TRUE);
         toggle_login_window (session, TRUE);
 
-        gdm_greeter_client_call_start_conversation (session->priv->client, "gdm");
-
         gdm_profile_end (NULL);
 
         return res;
@@ -573,6 +588,64 @@ gdm_greeter_session_event_handler (GdkEvent          *event,
 }
 
 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);
@@ -601,6 +674,10 @@ gdm_greeter_session_init (GdmGreeterSession *session)
                           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);
@@ -635,6 +712,8 @@ gdm_greeter_session_init (GdmGreeterSession *session)
         gdk_event_handler_set ((GdkEventFunc) gdm_greeter_session_event_handler,
                                session, NULL);
 
+
+        load_plugins (session);
         gdm_profile_end (NULL);
 }
 
diff --git a/gui/simple-greeter/gdm-plugin-manager.c b/gui/simple-greeter/gdm-plugin-manager.c
new file mode 100644
index 0000000..49e442c
--- /dev/null
+++ b/gui/simple-greeter/gdm-plugin-manager.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gdm-plugin-manager.h"
+#include "gdm-greeter-extension.h"
+
+#define GDM_PLUGIN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerPrivate))
+
+typedef struct
+{
+        GModule             *module;
+        char                *filename;
+        GdmGreeterExtension *extension;
+} GdmPluginManagerPlugin;
+
+typedef struct
+{
+        GdmPluginManager *manager;
+        GCancellable     *cancellable;
+} GdmPluginManagerOperation;
+
+struct GdmPluginManagerPrivate
+{
+        GHashTable      *plugins;
+
+        GFileMonitor    *plugin_dir_monitor;
+        GList           *pending_operations;
+};
+
+enum {
+        PLUGINS_LOADED,
+        PLUGIN_ADDED,
+        PLUGIN_REMOVED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void     gdm_plugin_manager_class_init (GdmPluginManagerClass *klass);
+static void     gdm_plugin_manager_init       (GdmPluginManager      *plugin_manager);
+static void     gdm_plugin_manager_finalize   (GObject             *object);
+
+static GObject *plugin_manager_object = NULL;
+
+G_DEFINE_TYPE (GdmPluginManager, gdm_plugin_manager, G_TYPE_OBJECT)
+
+static GdmPluginManagerOperation *
+start_operation (GdmPluginManager *manager)
+{
+        GdmPluginManagerOperation *operation;
+
+        operation = g_new0 (GdmPluginManagerOperation, 1);
+        operation->cancellable = g_cancellable_new ();
+        operation->manager = manager;
+
+        return operation;
+}
+
+static void
+free_operation (GdmPluginManagerOperation *operation)
+{
+        if (operation->cancellable != NULL) {
+                g_object_unref (operation->cancellable);
+                operation->cancellable = NULL;
+        }
+        g_free (operation);
+}
+
+static void
+cancel_operation (GdmPluginManagerOperation *operation)
+{
+        if (operation->cancellable != NULL &&
+            !g_cancellable_is_cancelled (operation->cancellable)) {
+                g_cancellable_cancel (operation->cancellable);
+        }
+
+        free_operation (operation);
+}
+
+static void
+gdm_plugin_manager_track_operation (GdmPluginManager          *manager,
+                                    GdmPluginManagerOperation *operation)
+{
+        manager->priv->pending_operations =
+            g_list_prepend (manager->priv->pending_operations, operation);
+}
+
+static void
+gdm_plugin_manager_untrack_operation (GdmPluginManager          *manager,
+                                     GdmPluginManagerOperation *operation)
+{
+        manager->priv->pending_operations =
+            g_list_remove (manager->priv->pending_operations, operation);
+}
+
+static void
+gdm_plugin_manager_cancel_pending_operations (GdmPluginManager *manager)
+{
+        GList *node;
+
+        node = manager->priv->pending_operations;
+        while (node != NULL) {
+                GList *next_node;
+                GdmPluginManagerOperation *operation;
+
+                operation = node->data;
+                next_node = node->next;
+
+                cancel_operation (operation);
+                manager->priv->pending_operations =
+                    g_list_delete_link (manager->priv->pending_operations,
+                                        node);
+
+                node = next_node;
+        }
+}
+
+static void
+gdm_plugin_manager_class_init (GdmPluginManagerClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gdm_plugin_manager_finalize;
+
+        signals [PLUGINS_LOADED] =
+                g_signal_new ("plugins-loaded",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmPluginManagerClass, plugins_loaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [PLUGIN_ADDED] =
+                g_signal_new ("plugin-added",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmPluginManagerClass, plugin_added),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_GREETER_PLUGIN);
+        signals [PLUGIN_REMOVED] =
+                g_signal_new ("plugin-removed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmPluginManagerClass, plugin_removed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_GREETER_PLUGIN);
+
+        g_type_class_add_private (klass, sizeof (GdmPluginManagerPrivate));
+}
+
+static void
+on_plugin_loaded (GdmPluginManager *manager,
+                  GdmGreeterPlugin *plugin)
+{
+        g_debug ("GdmPluginManager: plugin '%s' loaded.",
+                 gdm_greeter_plugin_get_filename (plugin));
+        g_signal_emit (manager, signals [PLUGIN_ADDED], 0, plugin);
+}
+
+static void
+on_plugin_load_failed (GdmPluginManager *manager,
+                       GdmGreeterPlugin *plugin)
+{
+        const char *filename;
+
+        g_debug ("GdmPluginManager: plugin '%s' could not be loaded.",
+                 gdm_greeter_plugin_get_filename (plugin));
+        filename = gdm_greeter_plugin_get_filename (plugin);
+        g_hash_table_remove (manager->priv->plugins, filename);
+}
+
+static void
+on_plugin_unloaded (GdmPluginManager       *manager,
+                    GdmGreeterPlugin *plugin)
+{
+        const char *filename;
+
+        filename = gdm_greeter_plugin_get_filename (plugin);
+        g_hash_table_remove (manager->priv->plugins, filename);
+}
+
+static void
+load_plugin (GdmPluginManager *manager,
+             const char       *filename)
+{
+        GdmGreeterPlugin *plugin;
+
+        g_debug ("GdmPluginManager: loading plugin '%s'", filename);
+
+        plugin = gdm_greeter_plugin_new (filename);
+
+        g_signal_connect_swapped (plugin, "loaded",
+                                  G_CALLBACK (on_plugin_loaded),
+                                  manager);
+        g_signal_connect_swapped (plugin, "load-failed",
+                                  G_CALLBACK (on_plugin_load_failed),
+                                  manager);
+        g_signal_connect_swapped (plugin, "unloaded",
+                                  G_CALLBACK (on_plugin_unloaded),
+                                  manager);
+        g_hash_table_insert (manager->priv->plugins,
+                             g_strdup (filename), plugin);
+
+        gdm_greeter_plugin_load (plugin);
+}
+
+static void
+on_plugin_info_read (GFileEnumerator           *enumerator,
+                     GAsyncResult              *result,
+                     GdmPluginManagerOperation *operation)
+{
+        GdmPluginManager *manager;
+        GFile *plugin_dir_file;
+        GList *file_list, *node;
+        GError *error;
+
+        manager = operation->manager;
+        error = NULL;
+        file_list = g_file_enumerator_next_files_finish (enumerator,
+                                                         result, &error);
+        plugin_dir_file = g_file_enumerator_get_container (enumerator);
+        if (error != NULL) {
+                char  *plugin_dir;
+
+                plugin_dir = g_file_get_parse_name (plugin_dir_file);
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_debug ("GdmPluginManager: Cancelled reading plugin directory %s",
+                                 plugin_dir);
+                } else {
+                        g_warning ("GdmPluginManager: Unable to read plugin directory %s: %s",
+                                   plugin_dir, error->message);
+                }
+                g_free (plugin_dir);
+                g_error_free (error);
+                g_object_unref (plugin_dir_file);
+                gdm_plugin_manager_untrack_operation (manager, operation);
+                return;
+        }
+
+#ifndef PLUGIN_ORDERING_FIGURED_OUT
+        node = file_list;
+        while (node != NULL) {
+                GFileInfo *info;
+                GFile *file;
+                char *path;
+                GList *next_node;
+
+                next_node = node->next;
+
+                info = (GFileInfo *) node->data;
+
+                file = g_file_get_child (plugin_dir_file,
+                                         g_file_info_get_name (info));
+                path = g_file_get_path (file);
+
+                if (g_str_has_suffix (path, "password.so")) {
+                        file_list = g_list_delete_link (file_list, node);
+                        file_list = g_list_prepend (file_list, info);
+                        next_node = NULL;
+                }
+                g_free (path);
+                g_object_unref (file);
+
+                node = next_node;
+        }
+#endif
+
+        node = file_list;
+        while (node != NULL) {
+                GFileInfo *info;
+                GFile *file;
+                char *path;
+
+                info = (GFileInfo *) node->data;
+
+                file = g_file_get_child (plugin_dir_file,
+                                         g_file_info_get_name (info));
+                path = g_file_get_path (file);
+
+                if (g_str_has_suffix (path, G_MODULE_SUFFIX)) {
+                        load_plugin (manager, path);
+                }
+                g_free (path);
+                g_object_unref (file);
+
+                node = node->next;
+        }
+        g_object_unref (plugin_dir_file);
+
+        gdm_plugin_manager_untrack_operation (manager, operation);
+        g_signal_emit (manager, signals [PLUGINS_LOADED], 0);
+
+        g_list_free (file_list);
+}
+
+static void
+on_plugin_dir_opened (GFile                     *plugin_dir_file,
+                      GAsyncResult              *result,
+                      GdmPluginManagerOperation *open_operation)
+{
+        GdmPluginManager *manager;
+        GFileEnumerator *enumerator;
+        GError *error;
+        GdmPluginManagerOperation *operation;
+
+        manager = open_operation->manager;
+        gdm_plugin_manager_untrack_operation (manager, open_operation);
+
+        error = NULL;
+        enumerator = g_file_enumerate_children_finish (plugin_dir_file,
+                                                       result, &error);
+
+        if (enumerator == NULL) {
+                char *plugin_dir;
+
+                plugin_dir = g_file_get_parse_name (plugin_dir_file);
+
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_debug ("GdmPluginManager: Cancelled opening plugin directory %s",
+                                 plugin_dir);
+                } else {
+                        g_warning ("GdmPluginManager: Unable to open plugin directory %s: %s",
+                                   plugin_dir, error->message);
+                }
+                g_free (plugin_dir);
+                g_error_free (error);
+                return;
+        }
+
+        operation = start_operation (manager);
+
+        g_file_enumerator_next_files_async (enumerator, G_MAXINT,
+                                            G_PRIORITY_DEFAULT,
+                                            operation->cancellable,
+                                            (GAsyncReadyCallback)
+                                            on_plugin_info_read,
+                                            operation);
+
+        gdm_plugin_manager_track_operation (manager, operation);
+}
+
+static void
+load_plugins_in_dir (GdmPluginManager *manager,
+                     const char       *plugin_dir)
+{
+        GFile *plugin_dir_file;
+        GdmPluginManagerOperation *operation;
+
+        g_debug ("GdmPluginManager: loading plugins in dir '%s'", plugin_dir);
+
+        operation = start_operation (manager);
+        plugin_dir_file = g_file_new_for_path (plugin_dir);
+        g_file_enumerate_children_async (plugin_dir_file, "standard::*",
+                                         G_FILE_QUERY_INFO_NONE,
+                                         G_PRIORITY_DEFAULT,
+                                         operation->cancellable,
+                                         (GAsyncReadyCallback)
+                                         on_plugin_dir_opened,
+                                         operation);
+        g_object_unref (plugin_dir_file);
+        gdm_plugin_manager_track_operation (manager, operation);
+}
+
+static void
+on_plugin_dir_changed (GFileMonitor              *monitor,
+                       GFile                     *file,
+                       GFile                     *other_file,
+                       GFileMonitorEvent          event_type,
+                       GdmPluginManagerOperation *operation)
+{
+}
+
+static void
+watch_plugin_dir (GdmPluginManager *manager,
+                  const char       *plugin_dir)
+{
+
+        GdmPluginManagerOperation *operation;
+        GFile  *file;
+        GError *error;
+
+        operation = start_operation (manager);
+
+        file = g_file_new_for_path (plugin_dir);
+        manager->priv->plugin_dir_monitor = g_file_monitor_directory (file,
+                                                                      G_FILE_MONITOR_NONE,
+                                                                      operation->cancellable,
+                                                                      &error);
+        if (manager->priv->plugin_dir_monitor != NULL) {
+                g_signal_connect (manager->priv->plugin_dir_monitor,
+                                  "changed",
+                                  G_CALLBACK (on_plugin_dir_changed),
+                                  operation);
+                gdm_plugin_manager_track_operation (manager, operation);
+        } else {
+                g_warning ("Unable to monitor %s: %s",
+                           plugin_dir, error->message);
+                g_error_free (error);
+                free_operation (operation);
+        }
+        g_object_unref (file);
+}
+
+static void
+gdm_plugin_manager_init (GdmPluginManager *manager)
+{
+        manager->priv = GDM_PLUGIN_MANAGER_GET_PRIVATE (manager);
+
+        manager->priv->plugins = g_hash_table_new_full (g_str_hash,
+                                                        g_str_equal,
+                                                        g_free,
+                                                        g_object_unref);
+        watch_plugin_dir (manager, GDM_SIMPLE_GREETER_PLUGINS_DIR);
+        load_plugins_in_dir (manager, GDM_SIMPLE_GREETER_PLUGINS_DIR);
+}
+
+static void
+gdm_plugin_manager_finalize (GObject *object)
+{
+        GdmPluginManager *manager;
+
+        manager = GDM_PLUGIN_MANAGER (object);
+
+        g_hash_table_destroy (manager->priv->plugins);
+        g_file_monitor_cancel (manager->priv->plugin_dir_monitor);
+
+        gdm_plugin_manager_cancel_pending_operations (manager);
+
+        G_OBJECT_CLASS (gdm_plugin_manager_parent_class)->finalize (object);
+}
+
+GdmPluginManager *
+gdm_plugin_manager_ref_default (void)
+{
+        if (plugin_manager_object != NULL) {
+                g_object_ref (plugin_manager_object);
+        } else {
+                plugin_manager_object = g_object_new (GDM_TYPE_PLUGIN_MANAGER, NULL);
+                g_object_add_weak_pointer (plugin_manager_object,
+                                           (gpointer *) &plugin_manager_object);
+        }
+
+        return GDM_PLUGIN_MANAGER (plugin_manager_object);
+}
+
+GdmGreeterPlugin *
+gdm_plugin_manager_get_plugin (GdmPluginManager *manager,
+                               const char       *name)
+{
+        return g_hash_table_lookup (manager->priv->plugins, name);
+}
diff --git a/gui/simple-greeter/gdm-plugin-manager.h b/gui/simple-greeter/gdm-plugin-manager.h
new file mode 100644
index 0000000..f181140
--- /dev/null
+++ b/gui/simple-greeter/gdm-plugin-manager.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#ifndef __GDM_PLUGIN_MANAGER_H
+#define __GDM_PLUGIN_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gdm-greeter-plugin.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_PLUGIN_MANAGER         (gdm_plugin_manager_get_type ())
+#define GDM_PLUGIN_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManager))
+#define GDM_PLUGIN_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerClass))
+#define GDM_IS_PLUGIN_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_PLUGIN_MANAGER))
+#define GDM_IS_PLUGIN_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_PLUGIN_MANAGER))
+#define GDM_PLUGIN_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerClass))
+
+typedef struct GdmPluginManagerPrivate GdmPluginManagerPrivate;
+
+typedef struct
+{
+        GObject                parent;
+        GdmPluginManagerPrivate *priv;
+} GdmPluginManager;
+
+typedef struct
+{
+        GObjectClass   parent_class;
+
+        void          (* plugins_loaded)              (GdmPluginManager *plugin_manager);
+        void          (* plugin_added)                (GdmPluginManager *plugin_manager,
+                                                       GdmGreeterPlugin *plugin);
+        void          (* plugin_removed)              (GdmPluginManager *plugin_manager,
+                                                       GdmGreeterPlugin *plugin);
+} GdmPluginManagerClass;
+
+GType             gdm_plugin_manager_get_type              (void);
+
+GdmPluginManager *gdm_plugin_manager_ref_default           (void);
+
+GdmGreeterPlugin *gdm_plugin_manager_get_plugin            (GdmPluginManager *plugin,
+                                                            const char       *name);
+
+G_END_DECLS
+
+#endif /* __GDM_PLUGIN_MANAGER_H */
diff --git a/gui/simple-greeter/gdm-task-list.c b/gui/simple-greeter/gdm-task-list.c
index e0fd3d4..3dff10e 100644
--- a/gui/simple-greeter/gdm-task-list.c
+++ b/gui/simple-greeter/gdm-task-list.c
@@ -37,12 +37,6 @@
 
 #define GDM_TASK_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_TASK_LIST, GdmTaskListPrivate))
 
-typedef struct
-{
-        GtkWidget *radio_button;
-        char      *name;
-} GdmTask;
-
 struct GdmTaskListPrivate
 {
         GtkWidget *box;
@@ -59,54 +53,162 @@ 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);
+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 *radio_button)
+                 GtkRadioButton *button)
 {
         GdmTask *task;
 
-        task = g_object_get_data (G_OBJECT (radio_button), "gdm-task");
+        task = g_object_get_data (G_OBJECT (button), "gdm-task");
 
-        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_button))) {
-                g_signal_emit (widget, signals[ACTIVATED], 0, task->name);
+        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->name);
+                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,
-                        const char  *name,
-                        const char  *icon_name)
+                        GdmTask     *task)
 {
-        GdmTask   *task;
         GtkWidget *image;
+        GtkWidget *button;
+        GIcon     *icon;
+        char      *description;
 
-        task = g_new0 (GdmTask, 1);
-
-        task->name = g_strdup (name);
         if (task_list->priv->tasks == NULL) {
-                task->radio_button = gtk_radio_button_new (NULL);
+                button = gtk_radio_button_new (NULL);
         } else {
-                task->radio_button = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (((GdmTask *) task_list->priv->tasks->data)->radio_button));
+                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 (task->radio_button, "draw-indicator", FALSE, NULL);
-        g_object_set_data (G_OBJECT (task->radio_button), "gdm-task", task);
-        g_signal_connect_swapped (task->radio_button,
-                                  "toggled", G_CALLBACK (on_task_toggled), task_list);
-        image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DND);
-        gtk_widget_show (image);
-        gtk_container_add (GTK_CONTAINER (task->radio_button), image);
-        gtk_widget_show (task->radio_button);
-        gtk_container_add (GTK_CONTAINER (task->radio_button), task_list->priv->box);
+        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);
 
-        gtk_container_add (GTK_CONTAINER (task_list->priv->box), task->radio_button);
-        task_list->priv->tasks = g_list_append (task_list->priv->tasks, task);
+        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,
+                                                g_object_ref (task));
+
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+                g_signal_emit (task_list, signals[ACTIVATED], 0, task);
+        }
 }
 
 static void
@@ -122,9 +224,9 @@ gdm_task_list_class_init (GdmTaskListClass *klass)
                                             G_STRUCT_OFFSET (GdmTaskListClass, activated),
                                             NULL,
                                             NULL,
-                                            g_cclosure_marshal_VOID__STRING,
+                                            g_cclosure_marshal_VOID__OBJECT,
                                             G_TYPE_NONE,
-                                            1, G_TYPE_STRING);
+                                            1, G_TYPE_OBJECT);
 
         signals [DEACTIVATED] = g_signal_new ("deactivated",
                                             G_TYPE_FROM_CLASS (object_class),
@@ -132,22 +234,19 @@ gdm_task_list_class_init (GdmTaskListClass *klass)
                                             G_STRUCT_OFFSET (GdmTaskListClass, deactivated),
                                             NULL,
                                             NULL,
-                                            g_cclosure_marshal_VOID__STRING,
+                                            g_cclosure_marshal_VOID__OBJECT,
                                             G_TYPE_NONE,
-                                            1, G_TYPE_STRING);
+                                            1, G_TYPE_OBJECT);
 
         g_type_class_add_private (klass, sizeof (GdmTaskListPrivate));
-}
-
-static void
-gdm_task_list_init (GdmTaskList *widget)
+} 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 (FALSE, 2);
+        widget->priv->box = gtk_hbox_new (TRUE, 2);
         gtk_widget_show (widget->priv->box);
         gtk_container_add (GTK_CONTAINER (widget),
                            widget->priv->box);
@@ -163,7 +262,7 @@ gdm_task_list_finalize (GObject *object)
 
         widget = GDM_TASK_LIST (object);
 
-        g_list_foreach (widget->priv->tasks, (GFunc) g_free, NULL);
+        g_list_foreach (widget->priv->tasks, (GFunc) g_object_unref, NULL);
         g_list_free (widget->priv->tasks);
 
         G_OBJECT_CLASS (gdm_task_list_parent_class)->finalize (object);
@@ -179,20 +278,46 @@ gdm_task_list_new (void)
         return GTK_WIDGET (object);
 }
 
-const char *
+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)
 {
-        GList *node;
+        return gdm_task_list_foreach_task (widget,
+                                        (GdmTaskListForeachFunc)
+                                        gdm_task_list_task_is_active,
+                                        NULL);
+}
 
-        for (node = widget->priv->tasks; node != NULL; node = node->next) {
-                GdmTask *task;
+gboolean
+gdm_task_list_set_active_task (GdmTaskList *widget,
+                               GdmTask     *task)
+{
+        GtkWidget *button;
 
-                task = node->data;
+        button = GTK_WIDGET (g_object_get_data (G_OBJECT (task),
+                             "gdm-task-list-button"));
 
-                if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (task->radio_button)) ) {
-                        return task->name;
+        if (GTK_WIDGET_IS_SENSITIVE (button)) {
+                if (gtk_widget_activate (button)) {
+                        return TRUE;
                 }
         }
 
-        return NULL;
+        return FALSE;
+}
+
+int
+gdm_task_list_get_number_of_tasks (GdmTaskList *widget)
+{
+        return g_list_length (widget->priv->tasks);
 }
diff --git a/gui/simple-greeter/gdm-task-list.h b/gui/simple-greeter/gdm-task-list.h
index ade21b6..8bc0c0e 100644
--- a/gui/simple-greeter/gdm-task-list.h
+++ b/gui/simple-greeter/gdm-task-list.h
@@ -22,8 +22,11 @@
 #define __GDM_TASK_LIST_H
 
 #include <glib-object.h>
+#include <gio/gio.h>
 #include <gtk/gtkalignment.h>
 
+#include "gdm-task.h"
+
 G_BEGIN_DECLS
 
 #define GDM_TYPE_TASK_LIST         (gdm_task_list_get_type ())
@@ -34,31 +37,44 @@ G_BEGIN_DECLS
 #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 struct
+typedef gboolean (* GdmTaskListForeachFunc) (GdmTaskList *task_list,
+                                            GdmTask     *task,
+                                            gpointer     data);
+
+struct _GdmTaskList
 {
         GtkAlignment             parent;
         GdmTaskListPrivate *priv;
-} GdmTaskList;
+};
 
 typedef struct
 {
         GtkAlignmentClass       parent_class;
 
         void (* deactivated)      (GdmTaskList *widget,
-                                   const char  *name);
+                                   GdmTask     *task);
         void (* activated)      (GdmTaskList *widget,
-                                 const char  *name);
+                                 GdmTask     *task);
 } GdmTaskListClass;
 
+GType       gdm_task_list_get_type               (void);
+GtkWidget * gdm_task_list_new                    (void);
+
 
-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);
 
-const char *           gdm_task_list_get_active_task (GdmTaskList *widget);
-void                   gdm_task_list_add_task (GdmTaskList *widget,
-                                               const char  *name,
-                                               const char  *icon_name);
+int         gdm_task_list_get_number_of_tasks (GdmTaskList *widget);
 G_END_DECLS
 
 #endif /* __GDM_TASK_LIST_H */
diff --git a/gui/simple-greeter/libgdmsimplegreeter/Makefile.am b/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
new file mode 100644
index 0000000..1ef5725
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
@@ -0,0 +1,46 @@
+NULL =
+
+AM_CPPFLAGS = \
+	-I.					\
+	-I..					\
+	-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)				\
+	$(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 --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
new file mode 100644
index 0000000..e21c56b
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+
+enum {
+        ANSWER,
+        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);
+}
+
+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);
+}
+
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
new file mode 100644
index 0000000..f1910cf
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+
+#ifndef __GDM_CONVERSATION_H
+#define __GDM_CONVERSATION_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_CONVERSATION         (gdm_conversation_get_type ())
+#define GDM_CONVERSATION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_CONVERSATION, GdmConversation))
+#define GDM_CONVERSATION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_CONVERSATION, GdmConversationIface))
+#define GDM_IS_CONVERSATION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_CONVERSATION))
+#define GDM_CONVERSATION_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_CONVERSATION, GdmConversationIface))
+
+#define GDM_CONVERSATION_DEFAULT_ACTION "default-action"
+
+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);
+};
+
+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);
+
+G_END_DECLS
+
+#endif /* __GDM_CONVERSATION_H */
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c
new file mode 100644
index 0000000..a71d3f7
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-greeter-extension.h"
+
+enum {
+        LOADED,
+        LOAD_FAILED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_greeter_extension_class_init (gpointer g_iface);
+
+GType
+gdm_greeter_extension_get_type (void)
+{
+        static GType greeter_extension_type = 0;
+
+        if (!greeter_extension_type) {
+                greeter_extension_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                                           "GdmGreeterExtension",
+                                                           sizeof (GdmGreeterExtensionIface),
+                                                           (GClassInitFunc) gdm_greeter_extension_class_init,
+                                                           0, NULL, 0);
+
+                g_type_interface_add_prerequisite (greeter_extension_type, G_TYPE_OBJECT);
+        }
+
+        return greeter_extension_type;
+}
+
+static void
+gdm_greeter_extension_class_init (gpointer g_iface)
+{
+        GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+        signals [LOADED] =
+                g_signal_new ("loaded",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterExtensionIface, loaded),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        signals [LOADED] =
+                g_signal_new ("load_failed",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterExtensionIface, load_failed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE,
+                              1, G_TYPE_POINTER);
+}
+
+void
+gdm_greeter_extension_loaded (GdmGreeterExtension *extension)
+{
+        g_signal_emit (extension, signals [LOADED], 0);
+}
+
+void
+gdm_greeter_extension_load_failed (GdmGreeterExtension *extension,
+                                   GError              *error)
+{
+        g_signal_emit (extension, signals [LOAD_FAILED], 0, error);
+}
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h
new file mode 100644
index 0000000..283ba0e
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 Red Hat, Inc.  *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode
+ */
+
+#ifndef __GDM_GREETER_EXTENSION_H
+#define __GDM_GREETER_EXTENSION_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_GREETER_EXTENSION         (gdm_greeter_extension_get_type ())
+#define GDM_GREETER_EXTENSION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_GREETER_EXTENSION, GdmGreeterExtension))
+#define GDM_GREETER_EXTENSION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_GREETER_EXTENSION, GdmGreeterExtensionClass))
+#define GDM_IS_GREETER_EXTENSION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_GREETER_EXTENSION))
+#define GDM_GREETER_EXTENSION_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_GREETER_EXTENSION, GdmGreeterExtensionIface))
+
+typedef struct _GdmGreeterExtension      GdmGreeterExtension;
+typedef struct _GdmGreeterExtensionIface GdmGreeterExtensionIface;
+
+struct _GdmGreeterExtensionIface
+{
+        GTypeInterface base_iface;
+
+        void (* loaded) (GdmGreeterExtension *extension);
+        void (* load_failed) (GdmGreeterExtension *extension,
+                              GError              *error);
+};
+
+GType gdm_greeter_extension_get_type (void) G_GNUC_CONST;
+
+void gdm_greeter_extension_loaded      (GdmGreeterExtension *extension);
+void gdm_greeter_extension_load_failed (GdmGreeterExtension *extension,
+                                        GError              *error);
+
+typedef GdmGreeterExtension * (* GdmGreeterPluginGetExtensionFunc) (void);
+
+G_END_DECLS
+#endif /* __GDM_GREETER_EXTENSION_H */
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
new file mode 100644
index 0000000..f72fa78
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-task.h"
+
+enum {
+        ENABLED,
+        DISABLED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+static void gdm_task_class_init (gpointer g_iface);
+
+GType
+gdm_task_get_type (void)
+{
+        static GType task_type = 0;
+
+        if (!task_type) {
+                task_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                                           "GdmTask",
+                                                           sizeof (GdmTaskIface),
+                                                           (GClassInitFunc) gdm_task_class_init,
+                                                           0, NULL, 0);
+
+                g_type_interface_add_prerequisite (task_type, G_TYPE_OBJECT);
+        }
+
+        return task_type;
+}
+
+GIcon *
+gdm_task_get_icon (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->get_icon (task);
+}
+
+char *
+gdm_task_get_description (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->get_description (task);
+}
+
+char *
+gdm_task_get_name (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->get_name (task);
+}
+
+void
+gdm_task_set_enabled (GdmTask   *task,
+                      gboolean   should_enable)
+{
+        g_object_set_data (G_OBJECT (task), "gdm-task-is-disabled", GINT_TO_POINTER (!should_enable));
+
+        if (should_enable) {
+                g_signal_emit (G_OBJECT (task), signals [ENABLED], 0);
+        } else {
+                g_signal_emit (G_OBJECT (task), signals [DISABLED], 0);
+        }
+}
+
+gboolean
+gdm_task_is_enabled (GdmTask   *task)
+{
+        return !g_object_get_data (G_OBJECT (task), "gdm-task-is-disabled");
+}
+
+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 --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
new file mode 100644
index 0000000..9894e65
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+
+#ifndef __GDM_TASK_H
+#define __GDM_TASK_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_TASK         (gdm_task_get_type ())
+#define GDM_TASK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_TASK, GdmTask))
+#define GDM_TASK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_TASK, GdmTaskIface))
+#define GDM_IS_TASK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_TASK))
+#define GDM_TASK_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_TASK, GdmTaskIface))
+
+typedef struct _GdmTask      GdmTask;
+typedef struct _GdmTaskIface GdmTaskIface;
+
+struct _GdmTaskIface
+{
+        GTypeInterface base_iface;
+
+        /* methods */
+        GIcon * (* get_icon)        (GdmTask   *task);
+        char *  (* get_description) (GdmTask   *task);
+        char *  (* get_name)        (GdmTask   *task);
+        /* 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);
+G_END_DECLS
+
+#endif /* __GDM_TASK_H */
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in b/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
new file mode 100644
index 0000000..a429d99
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
@@ -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 --git a/gui/simple-greeter/plugins/Makefile.am b/gui/simple-greeter/plugins/Makefile.am
new file mode 100644
index 0000000..c0390db
--- /dev/null
+++ b/gui/simple-greeter/plugins/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = password
diff --git a/gui/simple-greeter/plugins/password/Makefile.am b/gui/simple-greeter/plugins/password/Makefile.am
new file mode 100644
index 0000000..764904d
--- /dev/null
+++ b/gui/simple-greeter/plugins/password/Makefile.am
@@ -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 --git a/gui/simple-greeter/plugins/password/gdm-password-extension.c b/gui/simple-greeter/plugins/password/gdm-password-extension.c
new file mode 100644
index 0000000..3acab3f
--- /dev/null
+++ b/gui/simple-greeter/plugins/password/gdm-password-extension.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-password-extension.h"
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+struct _GdmPasswordExtensionPrivate
+{
+        GIcon     *icon;
+        GtkWidget *page;
+        GtkActionGroup *actions;
+
+        GtkWidget *message_label;
+        GtkWidget *prompt_label;
+        GtkWidget *prompt_entry;
+
+        guint      answer_pending : 1;
+};
+
+static void gdm_password_extension_finalize (GObject *object);
+
+static void gdm_task_iface_init (GdmTaskIface *iface);
+static void gdm_conversation_iface_init (GdmConversationIface *iface);
+static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmPasswordExtension,
+                         gdm_password_extension,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION,
+                                                gdm_greeter_extension_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK,
+                                                gdm_task_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION,
+                                                gdm_conversation_iface_init));
+
+static void
+gdm_password_extension_set_message (GdmConversation *conversation,
+                                    const char *message)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->message_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+gdm_password_extension_ask_question (GdmConversation *conversation,
+                                     const char      *message)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_password_extension_ask_secret (GdmConversation *conversation,
+                                   const char      *message)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_password_extension_reset (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        extension->priv->answer_pending = FALSE;
+
+        gdm_task_set_enabled (GDM_TASK (conversation), FALSE);
+}
+
+static void
+gdm_password_extension_set_ready (GdmConversation *conversation)
+{
+        gdm_task_set_enabled (GDM_TASK (conversation), TRUE);
+}
+
+char *
+gdm_password_extension_get_service_name (GdmConversation *conversation)
+{
+        return g_strdup (PAMSERVICENAME);
+}
+
+GtkWidget *
+gdm_password_extension_get_page (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        return extension->priv->page;
+}
+
+GtkActionGroup *
+gdm_password_extension_get_actions (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        return g_object_ref (extension->priv->actions);
+}
+
+void
+gdm_password_extension_request_answer (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        const char *text;
+
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return;
+        }
+
+        extension->priv->answer_pending = FALSE;
+        text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+        gdm_conversation_answer (conversation, text);
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+}
+
+gboolean
+gdm_password_extension_focus (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return FALSE;
+        }
+
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        return TRUE;
+}
+
+GIcon *
+gdm_password_extension_get_icon (GdmTask *task)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (task);
+        return g_object_ref (extension->priv->icon);
+}
+
+char *
+gdm_password_extension_get_name (GdmTask *task)
+{
+        return g_strdup (_("Password Authentication"));
+}
+
+char *
+gdm_password_extension_get_description (GdmTask *task)
+{
+        return g_strdup (_("Log into session with username and password"));
+}
+
+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;
+}
+
+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"), NULL, 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 --git a/gui/simple-greeter/plugins/password/gdm-password-extension.h b/gui/simple-greeter/plugins/password/gdm-password-extension.h
new file mode 100644
index 0000000..99fe17b
--- /dev/null
+++ b/gui/simple-greeter/plugins/password/gdm-password-extension.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_PASSWORD_EXTENSION_H
+#define __GDM_PASSWORD_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_PASSWORD_EXTENSION (gdm_password_extension_get_type ())
+#define GDM_PASSWORD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtension))
+#define GDM_PASSWORD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtensionClass))
+#define GDM_IS_PASSWORD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_PASSWORD_EXTENSION))
+#define GDM_IS_PASSWORD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_PASSWORD_EXTENSION))
+#define GDM_PASSWORD_EXTENSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtensionClass))
+
+typedef struct _GdmPasswordExtensionPrivate GdmPasswordExtensionPrivate;
+
+typedef struct
+{
+        GObject                  parent;
+        GdmPasswordExtensionPrivate *priv;
+} GdmPasswordExtension;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GdmPasswordExtensionClass;
+
+GType                 gdm_password_extension_get_type      (void);
+
+GdmPasswordExtension *gdm_password_extension_new       (void);
+
+G_END_DECLS
+
+#endif /* GDM_PASSWORD_EXTENSION_H */
diff --git a/gui/simple-greeter/plugins/password/gdm-password.pam b/gui/simple-greeter/plugins/password/gdm-password.pam
new file mode 100644
index 0000000..bac431d
--- /dev/null
+++ b/gui/simple-greeter/plugins/password/gdm-password.pam
@@ -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 --git a/gui/simple-greeter/plugins/password/page.ui b/gui/simple-greeter/plugins/password/page.ui
new file mode 100644
index 0000000..fe6da78
--- /dev/null
+++ b/gui/simple-greeter/plugins/password/page.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.14"/>
+    <object class="GtkVBox" id="page">
+      <property name="visible">True</property>
+      <property name="orientation">vertical</property>
+      <child>
+        <object class="GtkHBox" id="auth-input-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-prompt-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="expand">False</property>
+              <property name="fill">False</property>
+              <property name="position">0</property>
+            </packing>
+          </child>
+          <child>
+            <object class="GtkEntry" id="auth-prompt-entry">
+              <property name="visible">True</property>
+              <property name="can_focus">True</property>
+              <property name="activates_default">True</property>
+            </object>
+            <packing>
+              <property name="position">1</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">0</property>
+        </packing>
+      </child>
+      <child>
+        <object class="GtkHBox" id="auth-message-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-message-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="position">0</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">1</property>
+        </packing>
+      </child>
+    </object>
+</interface>
diff --git a/gui/simple-greeter/plugins/password/plugin.c b/gui/simple-greeter/plugins/password/plugin.c
new file mode 100644
index 0000000..9b87c67
--- /dev/null
+++ b/gui/simple-greeter/plugins/password/plugin.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include "gdm-password-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (void)
+{
+        static GObject *extension;
+
+        if (extension != NULL) {
+                g_object_ref (extension);
+        } else {
+                extension = g_object_new (GDM_TYPE_PASSWORD_EXTENSION, NULL);
+                g_object_add_weak_pointer (extension, (gpointer *) &extension);
+        }
+
+        return GDM_GREETER_EXTENSION (extension);
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1fccb90..7c344c9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -84,6 +84,7 @@ gui/simple-greeter/gdm-timer.c
 gui/simple-greeter/gdm-user.c
 gui/simple-greeter/gdm-user-chooser-widget.c
 gui/simple-greeter/greeter-main.c
+gui/simple-greeter/plugins/password/gdm-password-extension.c
 gui/user-switch-applet/applet.c
 gui/user-switch-applet/gdm-entry-menu-item.c
 gui/user-switch-applet/GNOME_FastUserSwitchApplet.server.in.in
-- 
1.7.0.1


From a05933382617361499c6c6faeb95ee7ecab29f58 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Sat, 7 Feb 2009 21:17:49 -0500
Subject: [PATCH 12/47] Force session reset if all PAM conversations fail

---
 gui/simple-greeter/gdm-greeter-login-window.c |   22 +++++++++++++++++++---
 1 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index a51184f..1b4fdef 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -661,15 +661,21 @@ reset_dialog (GdmGreeterLoginWindow *login_window)
 }
 
 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);
+}
+
 gboolean
 gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window,
                                 const char            *service_name)
@@ -715,6 +721,16 @@ gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_wind
                 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;
 }
 
-- 
1.7.0.1


From a8b44bd0bde6eda3c6cc4c1ffef47ccd5add0f3f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 18 Feb 2009 12:32:39 -0500
Subject: [PATCH 13/47] Add a way for plugins to pick users from list

The smartcard plugin is going to want to
start its conversation as soon as the card
gets plugged in.
---
 gui/simple-greeter/gdm-greeter-login-window.c      |   36 ++++++++++++++++++++
 .../libgdmsimplegreeter/gdm-conversation.c         |   32 +++++++++++++++++
 .../libgdmsimplegreeter/gdm-conversation.h         |    6 +++
 3 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 1b4fdef..3de9918 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1768,6 +1768,34 @@ on_conversation_answer (GdmGreeterLoginWindow *login_window,
         set_ready (login_window);
 }
 
+static void
+on_conversation_cancel (GdmGreeterLoginWindow *login_window,
+                        GdmConversation       *conversation)
+{
+        do_cancel (login_window);
+}
+
+static void
+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;
+        }
+
+        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);
+        }
+}
+
 void
 gdm_greeter_login_window_remove_extension (GdmGreeterLoginWindow *login_window,
  GdmGreeterExtension *extension)
@@ -1905,6 +1933,14 @@ gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
                                   "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));
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
index e21c56b..cef435c 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
@@ -30,6 +30,8 @@
 
 enum {
         ANSWER,
+        USER_CHOSEN,
+        CANCEL,
         LAST_SIGNAL
 };
 
@@ -71,6 +73,25 @@ gdm_conversation_class_init (gpointer g_iface)
                               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_FIRST,
+                              G_STRUCT_OFFSET (GdmConversationIface, user_chosen),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              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
@@ -145,3 +166,14 @@ gdm_conversation_answer (GdmConversation   *conversation,
         g_signal_emit (conversation, signals [ANSWER], 0, answer);
 }
 
+void
+gdm_conversation_cancel (GdmConversation   *conversation)
+{
+        g_signal_emit (conversation, signals [CANCEL], 0);
+}
+void
+gdm_conversation_choose_user (GdmConversation *conversation,
+                              const char      *username)
+{
+        g_signal_emit (conversation, signals [USER_CHOSEN], 0, username);
+}
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
index f1910cf..fb4bf49 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
 #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;
@@ -59,6 +60,8 @@ struct _GdmConversationIface
 
         /* signals */
         char * (* answer)       (GdmConversation *conversation);
+        void   (* cancel)       (GdmConversation *conversation);
+        void   (* user_chosen)     (GdmConversation *conversation);
 };
 
 GType  gdm_conversation_get_type     (void) G_GNUC_CONST;
@@ -81,6 +84,9 @@ gboolean   gdm_conversation_focus    (GdmConversation *conversation);
  */
 void   gdm_conversation_answer (GdmConversation   *conversation,
                                 const char        *answer);
+void   gdm_conversation_cancel (GdmConversation   *conversation);
+void   gdm_conversation_choose_user (GdmConversation   *conversation,
+                                     const char        *username);
 
 G_END_DECLS
 
-- 
1.7.0.1


From 194cb74d6503c46fc0f143bd0d9595230eb29762 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Feb 2009 14:05:20 -0500
Subject: [PATCH 14/47] Add new api to ask when chooser widget is done loading items

---
 gui/simple-greeter/gdm-chooser-widget.c |    8 ++++++++
 gui/simple-greeter/gdm-chooser-widget.h |    2 ++
 2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-chooser-widget.c b/gui/simple-greeter/gdm-chooser-widget.c
index 0f6859a..2282179 100644
--- a/gui/simple-greeter/gdm-chooser-widget.c
+++ b/gui/simple-greeter/gdm-chooser-widget.c
@@ -92,6 +92,7 @@ struct GdmChooserWidgetPrivate
 
         guint32                  should_hide_inactive_items : 1;
         guint32                  emit_activated_after_resize_animation : 1;
+        guint32                  is_loaded : 1;
 
         GdmChooserWidgetPosition separator_position;
         GdmChooserWidgetState    state;
@@ -2518,9 +2519,16 @@ gdm_chooser_widget_propagate_pending_key_events (GdmChooserWidget *widget)
         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)
 {
         gdm_chooser_widget_grow (widget);
+        widget->priv->is_loaded = TRUE;
         g_signal_emit (widget, signals[LOADED], 0);
 }
diff --git a/gui/simple-greeter/gdm-chooser-widget.h b/gui/simple-greeter/gdm-chooser-widget.h
index 7e3e59c..6a07843 100644
--- a/gui/simple-greeter/gdm-chooser-widget.h
+++ b/gui/simple-greeter/gdm-chooser-widget.h
@@ -136,6 +136,8 @@ int            gdm_chooser_widget_get_number_of_items          (GdmChooserWidget
 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);
-- 
1.7.0.1


From 6d7f9a643389d679869e2450b539deeea69f224f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 20 Feb 2009 14:31:27 -0500
Subject: [PATCH 15/47] Tell tasks they're ready only after user list loads

This way they won't try to access the list prematurely.
---
 gui/simple-greeter/gdm-greeter-login-window.c |   28 ++++++++++++++++++++++++-
 1 files changed, 27 insertions(+), 1 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 3de9918..c2463d9 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -676,6 +676,27 @@ do_cancel (GdmGreeterLoginWindow *login_window)
         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,
                                 const char            *service_name)
@@ -690,7 +711,12 @@ gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window,
                                            (gpointer) service_name);
 
         if (task != NULL) {
-                gdm_conversation_set_ready (GDM_CONVERSATION (task));
+                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);
         }
 
-- 
1.7.0.1


From a83b0111d9c8a1035eab2746488bdf28402d0cc0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 6 Feb 2009 16:25:47 -0500
Subject: [PATCH 16/47] Add fingerprint plugin

This commit adds a plugin to initiate a conversation for
fingerprint scans.
---
 configure.ac                                       |    4 +
 gui/simple-greeter/plugins/Makefile.am             |    2 +-
 gui/simple-greeter/plugins/fingerprint/Makefile.am |   56 ++++
 .../fingerprint/gdm-fingerprint-extension.c        |  299 ++++++++++++++++++++
 .../fingerprint/gdm-fingerprint-extension.h        |   56 ++++
 .../plugins/fingerprint/gdm-fingerprint.pam        |   17 ++
 .../plugins/fingerprint/icons/16x16/Makefile.am    |    5 +
 .../fingerprint/icons/16x16/gdm-fingerprint.png    |  Bin 0 -> 461 bytes
 .../plugins/fingerprint/icons/48x48/Makefile.am    |    5 +
 .../fingerprint/icons/48x48/gdm-fingerprint.png    |  Bin 0 -> 1638 bytes
 .../plugins/fingerprint/icons/Makefile.am          |    1 +
 gui/simple-greeter/plugins/fingerprint/page.ui     |   56 ++++
 gui/simple-greeter/plugins/fingerprint/plugin.c    |   40 +++
 13 files changed, 540 insertions(+), 1 deletions(-)
 create mode 100644 gui/simple-greeter/plugins/fingerprint/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
 create mode 100644 gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h
 create mode 100644 gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam
 create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/16x16/gdm-fingerprint.png
 create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/48x48/gdm-fingerprint.png
 create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/fingerprint/page.ui
 create mode 100644 gui/simple-greeter/plugins/fingerprint/plugin.c

diff --git a/configure.ac b/configure.ac
index e75bc93..29239a7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1435,6 +1435,10 @@ 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-chooser/Makefile
 gui/user-switch-applet/Makefile
 utils/Makefile
diff --git a/gui/simple-greeter/plugins/Makefile.am b/gui/simple-greeter/plugins/Makefile.am
index c0390db..9811a68 100644
--- a/gui/simple-greeter/plugins/Makefile.am
+++ b/gui/simple-greeter/plugins/Makefile.am
@@ -1 +1 @@
-SUBDIRS = password
+SUBDIRS = password fingerprint
diff --git a/gui/simple-greeter/plugins/fingerprint/Makefile.am b/gui/simple-greeter/plugins/fingerprint/Makefile.am
new file mode 100644
index 0000000..25fb6e8
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/Makefile.am
@@ -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 --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
new file mode 100644
index 0000000..b20dd65
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-fingerprint-extension.h"
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+struct _GdmFingerprintExtensionPrivate
+{
+        GIcon     *icon;
+        GtkWidget *page;
+        GtkActionGroup *actions;
+
+        GtkWidget *message_label;
+        GtkWidget *prompt_label;
+        GtkWidget *prompt_entry;
+
+        guint      answer_pending : 1;
+};
+
+static void gdm_fingerprint_extension_finalize (GObject *object);
+
+static void gdm_task_iface_init (GdmTaskIface *iface);
+static void gdm_conversation_iface_init (GdmConversationIface *iface);
+static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmFingerprintExtension,
+                         gdm_fingerprint_extension,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION,
+                                                gdm_greeter_extension_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK,
+                                                gdm_task_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION,
+                                                gdm_conversation_iface_init));
+
+static void
+gdm_fingerprint_extension_set_message (GdmConversation *conversation,
+                                       const char *message)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->message_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+gdm_fingerprint_extension_ask_question (GdmConversation *conversation,
+                                        const char      *message)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_fingerprint_extension_ask_secret (GdmConversation *conversation,
+                                      const char      *message)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_fingerprint_extension_reset (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        extension->priv->answer_pending = FALSE;
+
+        gdm_task_set_enabled (GDM_TASK (conversation), FALSE);
+}
+
+static void
+gdm_fingerprint_extension_set_ready (GdmConversation *conversation)
+{
+        gdm_task_set_enabled (GDM_TASK (conversation), TRUE);
+}
+
+char *
+gdm_fingerprint_extension_get_service_name (GdmConversation *conversation)
+{
+        return g_strdup (PAMSERVICENAME);
+}
+
+GtkWidget *
+gdm_fingerprint_extension_get_page (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        return extension->priv->page;
+}
+
+GtkActionGroup *
+gdm_fingerprint_extension_get_actions (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+
+        return g_object_ref (extension->priv->actions);
+}
+
+void
+gdm_fingerprint_extension_request_answer (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        const char *text;
+
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return;
+        }
+
+        extension->priv->answer_pending = FALSE;
+        text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+        gdm_conversation_answer (conversation, text);
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+}
+
+gboolean
+gdm_fingerprint_extension_focus (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+
+        if (!extension->priv->answer_pending) {
+                return FALSE;
+        }
+
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        return TRUE;
+}
+
+GIcon *
+gdm_fingerprint_extension_get_icon (GdmTask *task)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (task);
+        return g_object_ref (extension->priv->icon);
+}
+
+char *
+gdm_fingerprint_extension_get_name (GdmTask *task)
+{
+        return g_strdup (_("Fingerprint Authentication"));
+}
+
+char *
+gdm_fingerprint_extension_get_description (GdmTask *task)
+{
+        return g_strdup (_("Log into session with fingerprint"));
+}
+
+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;
+}
+
+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)
+{
+        GtkAction *action;
+
+        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 --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h
new file mode 100644
index 0000000..5d34b21
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_FINGERPRINT_EXTENSION_H
+#define __GDM_FINGERPRINT_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_FINGERPRINT_EXTENSION (gdm_fingerprint_extension_get_type ())
+#define GDM_FINGERPRINT_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtension))
+#define GDM_FINGERPRINT_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtensionClass))
+#define GDM_IS_FINGERPRINT_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_FINGERPRINT_EXTENSION))
+#define GDM_IS_FINGERPRINT_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_FINGERPRINT_EXTENSION))
+#define GDM_FINGERPRINT_EXTENSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtensionClass))
+
+typedef struct _GdmFingerprintExtensionPrivate GdmFingerprintExtensionPrivate;
+
+typedef struct
+{
+        GObject                  parent;
+        GdmFingerprintExtensionPrivate *priv;
+} GdmFingerprintExtension;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GdmFingerprintExtensionClass;
+
+GType                 gdm_fingerprint_extension_get_type      (void);
+
+GdmFingerprintExtension *gdm_fingerprint_extension_new       (void);
+
+G_END_DECLS
+
+#endif /* GDM_FINGERPRINT_EXTENSION_H */
diff --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam
new file mode 100644
index 0000000..1a1c777
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam
@@ -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 --git a/gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am b/gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am
new file mode 100644
index 0000000..f42e317
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am
@@ -0,0 +1,5 @@
+iconsdir = $(datadir)/icons/hicolor/16x16/apps
+
+icons_DATA = gdm-fingerprint.png
+
+EXTRA_DIST = $(icons_DATA)
diff --git a/gui/simple-greeter/plugins/fingerprint/icons/16x16/gdm-fingerprint.png b/gui/simple-greeter/plugins/fingerprint/icons/16x16/gdm-fingerprint.png
new file mode 100644
index 0000000000000000000000000000000000000000..4438cee2895638422dd470b05214dfae07751c3c
GIT binary patch
literal 461
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y
z{B+)352QE?JR*x382FBWFymBhK53vJdx@v7EBj3rUJ*vkeVx{~fu{I+x;TbdoNm3m
z(R*5;$nlToe`n0t<L8)Z;i2FZ7%MKWxX6s%Tz2WglLtN5)GttNa+@x-TG69$)3gqb
zEI}KNMIsUwSCm-29zOWJ<<29;+M08pc0ZqY`un`%cRNiYyjJQjH3<{f_q!z%nLT~F
ze7sJ(w`$r)t;1@;Ugpw_N1ty#XJdWdk@0zfV*4}0s~j4?`?mB&b-j(tSggMEO6%*X
zm)I7D27lMxb5zEyP<;A^X7-9MeF=@Ms?WBsr_O0S^|pFPv-6)7%zsW*iT<s$omVPu
zTp=$w_uZUNrZxu;cSIa}U-QF_wMzfBMBB!Q+(+R&4^zEfmaztG-T!yj{I6&29bDcd
zGv2M3CtW`4t=o}j|Ihzmik3K##r)v$M$3*4KDpHGcNrqqOD|EC$=K$v&ZP86T}?_s
zx+2{Al;B6k%P+1lh$%0!`Ks@7B)euc<C>7U_A+O~xmQ|0`3{N*Pgg&ebxsLQ0N@R|
ASO5S3

literal 0
HcmV?d00001

diff --git a/gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am b/gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am
new file mode 100644
index 0000000..f4ab2a0
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am
@@ -0,0 +1,5 @@
+iconsdir = $(datadir)/icons/hicolor/48x48/apps
+
+icons_DATA = gdm-fingerprint.png
+
+EXTRA_DIST = $(icons_DATA)
diff --git a/gui/simple-greeter/plugins/fingerprint/icons/48x48/gdm-fingerprint.png b/gui/simple-greeter/plugins/fingerprint/icons/48x48/gdm-fingerprint.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd6f546c387db5dbce1a88cb4bb46c3bdaa7f4a5
GIT binary patch
literal 1638
zcmV-s2ATPZP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m_e01m_fl`9S#00007bV*G`2iXJ-
z6EzRg;agMy00rhrL_t(&-sPBGY!p=#$N%T<w!7Qy?otbuQouq>Dp+=0hNP*1SPbzi
z_#h8rz!1>~h>0&gm>3fi(P#*P#E(e);7KL17{vI2V$cRcYAxICZVOdtv6z0hl(w|p
zkIu~T!6|O1Q__!_#l-Wnv*+A%@A=(x?%aEaumF+2LW#)s>?en#7^-%IICZFQoAOI~
zo8sbPcOVeh$;|PrtgJ7?;qU}36z6;$H~iq<<MS8nUtP3uxi32pNi~jZ{jmo&o^GqY
z@bLJ)$##Cf{}6x|hzN?J1OdEaC4gf-PN8p{m3J*I{;+&SRnhRVBzjH^B0py_iq@|3
zsBTg2-PR|%KU2;EKtVyl5h4ocu0_4Qz3&*h6QNKjSXfx-?&;|no@*>(J^;e)KCp6W
zuuwE-q2*XBI_kP`{*8Kg`vO?zFL7t*`gd1+4}Vovm6M2;rMa#uD=YJ*_a#ZvK~2*d
zHBGy`e*Jpcc7RYdmbkqB)jkmb_x|gysT#UY_rfn0VNw3_)k$`2zj^bf*I1^xxw(0h
z&y|&x9sqmwzu69l<7wLg$bk}9jxXOuE>Lo+duOBvE^@)+&J*Mi6=wdaR4Qlkd2DQK
zBLMH@e`a23JAekL%&amP-u#>?ww^1!&?Yo6YtR@eP1CZcEjoweRtX6q?lsPj?En)h
zT9Ttf0||};0iI%0k_!4-`!P0tJtb7_41gSyE*7QD_*#Q)&~|`}Phm8f7;PR>ZXoH4
zPqTMj?8Wdv$M2$3J*6m$e_94LEszFKW-#B#$XiI67~j|a*M%!X?(5TReHXiv6UouH
z!h116MBZrw3URBfh{%`$#n?U=3_c-A(u)-p6@JSAVvnOEInuZ5Qs+-CMq47LbT>Sb
z`a=IZt%R7_1puX`r7i&X7|dga;2$NTuK~Q9NF)wg1^@u{k4@COy0345gQwIHk2v!u
z7o!luV*=m?0H4oSVjQ2Dr)*?I^itZwF0m8<0BZIi0hO*A)IM33SLn}yX|G`-2LPz5
zT4G}By|v$jLdyU=lm<6yIe;$BMun=X<@%sLllHlKu%#y1=}PNkXZlfxh#G7GVCEJR
z^?mxFi%r^30<1By^`!;;tRXwKwg3nr`b`2^r~l^7R6?4jtuwmC<MB>?@ot!$1YP1o
zLqq56%D{k$dW{}z&WuMAA|pUwYiny#pOoE(wl(eT?Fm}|h{$a7DZQOfd3kvWfF%ZX
z8vs;QRXLg2c&MwfbR#n?ShMZFK$4^<nYq%u>clp)?F9e^2M4zTC@@UY&$GUf1%tu6
z0entGGJtDD^g&}|<0k-khCWg==uga`f0>9>6Mdr|>@x=aKxt`dBoc|tE(1b{twgjH
zKpuemnfW70lFrDo9I*2y$jsfcEEfXUZkYX($B!RZXI(<fe8J>QvPD(ZGqNn_z$Otw
zyrw8hH2{yHL_eL=VJ;etUIuX31WZ;G<vqIqm{|s}$)LW{)YNot&Zp75yu4QdoHsE&
zCrQ#CWJs8~`ZjBCW`LTS8b#Bz-2lwqz3<DiyfQ-!-_H9!(A3m)m6^@zFGo?718Iw_
z*skcava(_ez0kZ3!0b!fj!-D{p#Hqsruh5A;qdr^0AjJ&p8$@UZNQqQeJn{*h=}&u
zRR053T`Aw3VJ)ly&;Y=12LKU`SOO4@Mmqr<LFT2)vP(TPAO7c)P)6ks%K&1rSO<W!
znT(#9Yc2h(!pt=pjDN!8@zhufKt!i92=HrNUEPSK0MTglx7nuE0{PCCFLO?(^LYTn
zHV2<@I-N&t0cdDwXaTUp_yx`4_kVRb93HR*Krf{qiD(Oe9vk9Ui0Hta?h*4DrLnQG
zURBji0NO1EI2wsWF4+zM0L{(KZOpuxh}tX)e%$GFzCP!h+(Jf-#bO<rrtJctTCj1Q
z42Q$r83oWG>MYu&znRxv+M;bgYC$)3cML!XF|g1+yk2k5oy@>O*FdVawl;pp004|G
k&_6!!?EgnH@c)&+0Y61Y+XKhC^Z)<=07*qoM6N<$f~|xG1poj5

literal 0
HcmV?d00001

diff --git a/gui/simple-greeter/plugins/fingerprint/icons/Makefile.am b/gui/simple-greeter/plugins/fingerprint/icons/Makefile.am
new file mode 100644
index 0000000..c20f10d
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/icons/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = 16x16 48x48
diff --git a/gui/simple-greeter/plugins/fingerprint/page.ui b/gui/simple-greeter/plugins/fingerprint/page.ui
new file mode 100644
index 0000000..fe6da78
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/page.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.14"/>
+    <object class="GtkVBox" id="page">
+      <property name="visible">True</property>
+      <property name="orientation">vertical</property>
+      <child>
+        <object class="GtkHBox" id="auth-input-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-prompt-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="expand">False</property>
+              <property name="fill">False</property>
+              <property name="position">0</property>
+            </packing>
+          </child>
+          <child>
+            <object class="GtkEntry" id="auth-prompt-entry">
+              <property name="visible">True</property>
+              <property name="can_focus">True</property>
+              <property name="activates_default">True</property>
+            </object>
+            <packing>
+              <property name="position">1</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">0</property>
+        </packing>
+      </child>
+      <child>
+        <object class="GtkHBox" id="auth-message-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-message-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="position">0</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">1</property>
+        </packing>
+      </child>
+    </object>
+</interface>
diff --git a/gui/simple-greeter/plugins/fingerprint/plugin.c b/gui/simple-greeter/plugins/fingerprint/plugin.c
new file mode 100644
index 0000000..5ea9925
--- /dev/null
+++ b/gui/simple-greeter/plugins/fingerprint/plugin.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include "gdm-fingerprint-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (void)
+{
+        static GObject *extension;
+
+        if (extension != NULL) {
+                g_object_ref (extension);
+        } else {
+                extension = g_object_new (GDM_TYPE_FINGERPRINT_EXTENSION, NULL);
+                g_object_add_weak_pointer (extension, (gpointer *) &extension);
+        }
+
+        return GDM_GREETER_EXTENSION (extension);
+}
-- 
1.7.0.1


From ca7f457579e0272249ce497d8f1feafa39cdc805 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 11 Feb 2009 08:47:52 -0500
Subject: [PATCH 17/47] Add start of a smartcard plugin

It contains a copy and paste of an old RHEL patch
I did a few years ago.
---
 configure.ac                                       |   11 +
 gui/simple-greeter/plugins/Makefile.am             |    2 +-
 gui/simple-greeter/plugins/smartcard/Makefile.am   |   77 ++
 .../plugins/smartcard/gdm-smartcard-extension.c    |  418 ++++++
 .../plugins/smartcard/gdm-smartcard-extension.h    |   56 +
 .../plugins/smartcard/gdm-smartcard-manager.c      | 1394 ++++++++++++++++++++
 .../plugins/smartcard/gdm-smartcard-manager.h      |   86 ++
 .../plugins/smartcard/gdm-smartcard-worker.c       |  167 +++
 .../plugins/smartcard/gdm-smartcard.c              |  558 ++++++++
 .../plugins/smartcard/gdm-smartcard.h              |   94 ++
 .../plugins/smartcard/gdm-smartcard.pam            |   18 +
 .../plugins/smartcard/icons/16x16/Makefile.am      |    5 +
 .../smartcard/icons/16x16/gdm-smartcard.png        |  Bin 0 -> 871 bytes
 .../plugins/smartcard/icons/48x48/Makefile.am      |    5 +
 .../smartcard/icons/48x48/gdm-smartcard.png        |  Bin 0 -> 4202 bytes
 .../plugins/smartcard/icons/Makefile.am            |    1 +
 gui/simple-greeter/plugins/smartcard/page.ui       |   56 +
 gui/simple-greeter/plugins/smartcard/plugin.c      |   40 +
 po/POTFILES.in                                     |    1 +
 19 files changed, 2988 insertions(+), 1 deletions(-)
 create mode 100644 gui/simple-greeter/plugins/smartcard/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard.c
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard.h
 create mode 100644 gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam
 create mode 100644 gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/smartcard/icons/16x16/gdm-smartcard.png
 create mode 100644 gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/smartcard/icons/48x48/gdm-smartcard.png
 create mode 100644 gui/simple-greeter/plugins/smartcard/icons/Makefile.am
 create mode 100644 gui/simple-greeter/plugins/smartcard/page.ui
 create mode 100644 gui/simple-greeter/plugins/smartcard/plugin.c

diff --git a/configure.ac b/configure.ac
index 29239a7..33620a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,7 @@ LIBCANBERRA_GTK_REQUIRED_VERSION=0.4
 #FONTCONFIG_REQUIRED_VERSION=2.6.0
 FONTCONFIG_REQUIRED_VERSION=2.5.0
 DEVKIT_POWER_REQUIRED_VERSION=008
+NSS_REQUIRED_VERSION=3.11.1
 
 EXTRA_COMPILE_WARNINGS(yes)
 
@@ -90,6 +91,12 @@ PKG_CHECK_MODULES(DAEMON,
 AC_SUBST(DAEMON_CFLAGS)
 AC_SUBST(DAEMON_LIBS)
 
+PKG_CHECK_MODULES(NSS,
+        nss >= $NSS_REQUIRED_VERSION
+)
+AC_SUBST(NSS_CFLAGS)
+AC_SUBST(NSS_LIBS)
+
 PKG_CHECK_MODULES(XLIB, x11 xau, ,
   [AC_PATH_XTRA
     if test "x$no_x" = xyes; then
@@ -1439,6 +1446,10 @@ 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 --git a/gui/simple-greeter/plugins/Makefile.am b/gui/simple-greeter/plugins/Makefile.am
index 9811a68..3dd336f 100644
--- a/gui/simple-greeter/plugins/Makefile.am
+++ b/gui/simple-greeter/plugins/Makefile.am
@@ -1 +1 @@
-SUBDIRS = password fingerprint
+SUBDIRS = password fingerprint smartcard
diff --git a/gui/simple-greeter/plugins/smartcard/Makefile.am b/gui/simple-greeter/plugins/smartcard/Makefile.am
new file mode 100644
index 0000000..1ccebda
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/Makefile.am
@@ -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 --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
new file mode 100644
index 0000000..6fa01fb
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-smartcard-extension.h"
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#ifndef GDM_SMARTCARD_WORKER_COMMAND
+#define GDM_SMARTCARD_WORKER_COMMAND LIBEXECDIR "/gdm-smartcard-worker"
+#endif
+
+struct _GdmSmartcardExtensionPrivate
+{
+        GIcon     *icon;
+        GtkWidget *page;
+        GtkActionGroup *actions;
+        GtkAction  *login_action;
+
+        GtkWidget *message_label;
+        GtkWidget *prompt_label;
+        GtkWidget *prompt_entry;
+
+        GPid       worker_pid;
+        int        number_of_tokens;
+
+        guint      answer_pending : 1;
+};
+
+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) {
+                        gdm_conversation_choose_user (GDM_CONVERSATION (extension),
+                                                      GDM_CONVERSATION_OTHER_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);
+          }
+}
+
+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"));
+}
+
+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;
+}
+
+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"), NULL, 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 --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h
new file mode 100644
index 0000000..285b51a
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_SMARTCARD_EXTENSION_H
+#define __GDM_SMARTCARD_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SMARTCARD_EXTENSION (gdm_smartcard_extension_get_type ())
+#define GDM_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtension))
+#define GDM_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass))
+#define GDM_IS_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD_EXTENSION))
+#define GDM_IS_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD_EXTENSION))
+#define GDM_SMARTCARD_EXTENSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass))
+
+typedef struct _GdmSmartcardExtensionPrivate GdmSmartcardExtensionPrivate;
+
+typedef struct
+{
+        GObject                  parent;
+        GdmSmartcardExtensionPrivate *priv;
+} GdmSmartcardExtension;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GdmSmartcardExtensionClass;
+
+GType                 gdm_smartcard_extension_get_type      (void);
+
+GdmSmartcardExtension *gdm_smartcard_extension_new       (void);
+
+G_END_DECLS
+
+#endif /* GDM_SMARTCARD_EXTENSION_H */
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c
new file mode 100644
index 0000000..e346a9c
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c
@@ -0,0 +1,1394 @@
+/* gdm-smartcard-manager.c - object for monitoring smartcard insertion and
+ *                           removal events
+ *
+ * Copyright (C) 2006, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode
+ */
+#define _GNU_SOURCE
+#include "gdm-smartcard-manager.h"
+
+#define GDM_SMARTCARD_ENABLE_INTERNAL_API
+#include "gdm-smartcard.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <prerror.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <secmod.h>
+#include <secerr.h>
+
+#ifndef GDM_SMARTCARD_MANAGER_DRIVER
+#define GDM_SMARTCARD_MANAGER_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so"
+#endif
+
+#ifndef GDM_SMARTCARD_MANAGER_NSS_DB
+#define GDM_SMARTCARD_MANAGER_NSS_DB SYSCONFDIR"/pki/nssdb"
+#endif
+
+#ifndef GDM_MAX_OPEN_FILE_DESCRIPTORS
+#define GDM_MAX_OPEN_FILE_DESCRIPTORS 1024
+#endif
+
+#ifndef GDM_OPEN_FILE_DESCRIPTORS_DIR
+#define GDM_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd"
+#endif
+
+typedef enum _GdmSmartcardManagerState GdmSmartcardManagerState;
+typedef struct _GdmSmartcardManagerWorker GdmSmartcardManagerWorker;
+
+enum _GdmSmartcardManagerState {
+        GDM_SMARTCARD_MANAGER_STATE_STOPPED = 0,
+        GDM_SMARTCARD_MANAGER_STATE_STARTING,
+        GDM_SMARTCARD_MANAGER_STATE_STARTED,
+        GDM_SMARTCARD_MANAGER_STATE_STOPPING,
+};
+
+struct _GdmSmartcardManagerPrivate {
+        GdmSmartcardManagerState state;
+        SECMODModule *module;
+        char        *module_path;
+
+        GSource *smartcard_event_source;
+        GPid smartcard_event_watcher_pid;
+        GHashTable *smartcards;
+
+        GThread    *worker_thread;
+
+        guint poll_timeout_id;
+
+        guint32 is_unstoppable : 1;
+        guint32 nss_is_loaded : 1;
+};
+
+struct _GdmSmartcardManagerWorker {
+        SECMODModule *module;
+        GHashTable *smartcards;
+        gint write_fd;
+
+        guint32 nss_is_loaded : 1;
+};
+
+static void gdm_smartcard_manager_finalize (GObject *object);
+static void gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *service_class);
+static void gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *service_class);
+static void gdm_smartcard_manager_set_property (GObject       *object,
+                                                guint          prop_id,
+                                                const GValue  *value,
+                                                GParamSpec    *pspec);
+static void gdm_smartcard_manager_get_property (GObject    *object,
+                                                guint       prop_id,
+                                                GValue     *value,
+                                                GParamSpec *pspec);
+static void gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager,
+                                                   const char          *module_path);
+static void gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager,
+                                                        GdmSmartcard        *card);
+static void gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager_class,
+                                                         GdmSmartcard        *card);
+static gboolean gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager);
+static void gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager);
+
+static gboolean gdm_smartcard_manager_create_worker (GdmSmartcardManager *manager,
+                                                     int *worker_fd, GThread **worker_thread);
+
+static GdmSmartcardManagerWorker * gdm_smartcard_manager_worker_new (gint write_fd);
+static void gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker);
+static gboolean gdm_open_pipe (gint *write_fd, gint *read_fd);
+static gboolean sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes);
+static gboolean sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes);
+static GdmSmartcard *sc_read_smartcard (gint fd, SECMODModule *module);
+static gboolean sc_write_smartcard (gint fd, GdmSmartcard *card);
+
+enum {
+        PROP_0 = 0,
+        PROP_MODULE_PATH,
+        NUMBER_OF_PROPERTIES
+};
+
+enum {
+        SMARTCARD_INSERTED = 0,
+        SMARTCARD_REMOVED,
+        ERROR,
+        NUMBER_OF_SIGNALS
+};
+
+static guint gdm_smartcard_manager_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (GdmSmartcardManager,
+               gdm_smartcard_manager,
+               G_TYPE_OBJECT);
+
+static void
+gdm_smartcard_manager_class_init (GdmSmartcardManagerClass *manager_class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (manager_class);
+
+        gobject_class->finalize = gdm_smartcard_manager_finalize;
+
+        gdm_smartcard_manager_class_install_signals (manager_class);
+        gdm_smartcard_manager_class_install_properties (manager_class);
+
+        g_type_class_add_private (manager_class,
+                                  sizeof (GdmSmartcardManagerPrivate));
+}
+
+static void
+gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *card_class)
+{
+        GObjectClass *object_class;
+        GParamSpec *param_spec;
+
+        object_class = G_OBJECT_CLASS (card_class);
+        object_class->set_property = gdm_smartcard_manager_set_property;
+        object_class->get_property = gdm_smartcard_manager_get_property;
+
+        param_spec = g_param_spec_string ("module-path", _("Module Path"),
+                                          _("path to smartcard PKCS #11 driver"),
+                                          NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_MODULE_PATH, param_spec);
+}
+
+static void
+gdm_smartcard_manager_set_property (GObject       *object,
+                                    guint          prop_id,
+                                    const GValue  *value,
+                                    GParamSpec    *pspec)
+{
+        GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object);
+
+        switch (prop_id) {
+                case PROP_MODULE_PATH:
+                        gdm_smartcard_manager_set_module_path (manager,
+                                                                   g_value_get_string (value));
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                        break;
+        }
+}
+
+static void
+gdm_smartcard_manager_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+        GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object);
+        char *module_path;
+
+        switch (prop_id) {
+                case PROP_MODULE_PATH:
+                        module_path = gdm_smartcard_manager_get_module_path (manager);
+                        g_value_set_string (value, module_path);
+                        g_free (module_path);
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                        break;
+        }
+}
+
+char *
+gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager)
+{
+        return manager->priv->module_path;
+}
+
+static void
+gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager,
+                                       const char          *module_path)
+{
+        if ((manager->priv->module_path == NULL) && (module_path == NULL)) {
+                return;
+        }
+
+        if (((manager->priv->module_path == NULL) ||
+         (module_path == NULL) ||
+         (strcmp (manager->priv->module_path, module_path) != 0))) {
+                g_free (manager->priv->module_path);
+                manager->priv->module_path = g_strdup (module_path);
+                g_object_notify (G_OBJECT (manager), "module-path");
+        }
+}
+
+static void
+gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager,
+                                            GdmSmartcard        *card)
+{
+        g_debug ("informing smartcard of its removal");
+        _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED);
+        g_debug ("done");
+}
+
+static void
+gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager,
+                                             GdmSmartcard        *card)
+{
+        g_debug ("informing smartcard of its insertion");
+
+        _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED);
+        g_debug ("done");
+
+}
+
+static void
+gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *manager_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (manager_class);
+
+        gdm_smartcard_manager_signals[SMARTCARD_INSERTED] =
+                g_signal_new ("smartcard-inserted",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSmartcardManagerClass,
+                                               smartcard_inserted),
+                              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+        manager_class->smartcard_inserted = gdm_smartcard_manager_card_inserted_handler;
+
+        gdm_smartcard_manager_signals[SMARTCARD_REMOVED] =
+                g_signal_new ("smartcard-removed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSmartcardManagerClass,
+                                               smartcard_removed),
+                              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+        manager_class->smartcard_removed = gdm_smartcard_manager_card_removed_handler;
+
+        gdm_smartcard_manager_signals[ERROR] =
+                g_signal_new ("error",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmSmartcardManagerClass, error),
+                              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+        manager_class->error = NULL;
+}
+
+static gboolean
+sc_slot_id_equal (CK_SLOT_ID *slot_id_1,
+                      CK_SLOT_ID *slot_id_2)
+{
+        g_assert (slot_id_1 != NULL);
+        g_assert (slot_id_2 != NULL);
+
+        return *slot_id_1 == *slot_id_2;
+}
+
+static gboolean
+sc_slot_id_hash (CK_SLOT_ID *slot_id)
+{
+        guint32 upper_bits, lower_bits;
+        gint temp;
+
+        if (sizeof (CK_SLOT_ID) == sizeof (gint)) {
+                return g_int_hash (slot_id);
+        }
+
+        upper_bits = ((*slot_id) >> 31) - 1;
+        lower_bits = (*slot_id) & 0xffffffff;
+
+        /* The upper bits are almost certainly always zero,
+         * so let's degenerate to g_int_hash for the
+         * (very) common case
+         */
+        temp = lower_bits + upper_bits;
+        return upper_bits + g_int_hash (&temp);
+}
+
+static void
+gdm_smartcard_manager_init (GdmSmartcardManager *manager)
+{
+        g_debug ("initializing smartcard manager");
+
+        manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+                                                     GDM_TYPE_SMARTCARD_MANAGER,
+                                                     GdmSmartcardManagerPrivate);
+        manager->priv->poll_timeout_id = 0;
+        manager->priv->is_unstoppable = FALSE;
+        manager->priv->module = NULL;
+
+        manager->priv->smartcards =
+                g_hash_table_new_full (g_str_hash,
+                                       g_str_equal,
+                                       (GDestroyNotify) g_free,
+                                       (GDestroyNotify) g_object_unref);
+
+        if (!g_thread_supported()) {
+        g_thread_init (NULL);
+        }
+
+}
+
+static void
+gdm_smartcard_manager_finalize (GObject *object)
+{
+        GdmSmartcardManager *manager;
+        GObjectClass *gobject_class;
+
+        manager = GDM_SMARTCARD_MANAGER (object);
+        gobject_class =
+                G_OBJECT_CLASS (gdm_smartcard_manager_parent_class);
+
+        gdm_smartcard_manager_stop_now (manager);
+
+        g_hash_table_destroy (manager->priv->smartcards);
+        manager->priv->smartcards = NULL;
+
+        gobject_class->finalize (object);
+}
+
+GQuark
+gdm_smartcard_manager_error_quark (void)
+{
+        static GQuark error_quark = 0;
+
+        if (error_quark == 0) {
+                error_quark = g_quark_from_static_string ("gdm-smartcard-manager-error-quark");
+        }
+
+        return error_quark;
+}
+
+GdmSmartcardManager *
+gdm_smartcard_manager_new (const char *module_path)
+{
+        GdmSmartcardManager *instance;
+
+        instance = GDM_SMARTCARD_MANAGER (g_object_new (GDM_TYPE_SMARTCARD_MANAGER,
+                                                        "module-path", module_path,
+                                                        NULL));
+
+        return instance;
+}
+
+static void
+gdm_smartcard_manager_emit_error (GdmSmartcardManager *manager,
+                                  GError              *error)
+{
+        manager->priv->is_unstoppable = TRUE;
+        g_signal_emit (manager, gdm_smartcard_manager_signals[ERROR], 0,
+                       error);
+        manager->priv->is_unstoppable = FALSE;
+}
+
+static void
+gdm_smartcard_manager_emit_smartcard_inserted (GdmSmartcardManager *manager,
+                                               GdmSmartcard        *card)
+{
+        manager->priv->is_unstoppable = TRUE;
+        g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_INSERTED], 0,
+                       card);
+        manager->priv->is_unstoppable = FALSE;
+}
+
+static void
+gdm_smartcard_manager_emit_smartcard_removed (GdmSmartcardManager *manager,
+                                              GdmSmartcard        *card)
+{
+        GdmSmartcardManagerState old_state;
+
+        old_state = manager->priv->state;
+        manager->priv->is_unstoppable = TRUE;
+        g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_REMOVED], 0,
+                       card);
+        manager->priv->is_unstoppable = FALSE;
+}
+
+static gboolean
+gdm_smartcard_manager_check_for_and_process_events (GIOChannel          *io_channel,
+                                                    GIOCondition         condition,
+                                                    GdmSmartcardManager *manager)
+{
+        GdmSmartcard *card;
+        gboolean should_stop;
+        guchar event_type;
+        char *card_name;
+        gint fd;
+
+        card = NULL;
+        should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR);
+
+        if (should_stop) {
+                g_debug ("received %s on event socket, stopping "
+                          "manager...",
+                          (condition & G_IO_HUP) && (condition & G_IO_ERR)?
+                          "error and hangup" :
+                          (condition & G_IO_HUP)?
+                          "hangup" : "error");
+        }
+
+        if (!(condition & G_IO_IN)) {
+                goto out;
+        }
+
+        fd = g_io_channel_unix_get_fd (io_channel);
+
+        event_type = '\0';
+        if (!sc_read_bytes (fd, &event_type, 1)) {
+                should_stop = TRUE;
+                goto out;
+        }
+
+        card = sc_read_smartcard (fd, manager->priv->module);
+
+        if (card == NULL) {
+                should_stop = TRUE;
+                goto out;
+        }
+
+        card_name = gdm_smartcard_get_name (card);
+
+        switch (event_type) {
+                case 'I':
+                        g_hash_table_replace (manager->priv->smartcards,
+                                              card_name, card);
+                        card_name = NULL;
+
+                        gdm_smartcard_manager_emit_smartcard_inserted (manager, card);
+                        card = NULL;
+                        break;
+
+                case 'R':
+                        gdm_smartcard_manager_emit_smartcard_removed (manager, card);
+                        if (!g_hash_table_remove (manager->priv->smartcards, card_name)) {
+                                g_debug ("got removal event of unknown card!");
+                        }
+                        g_free (card_name);
+                        card_name = NULL;
+                        card = NULL;
+                        break;
+
+                default:
+                        g_free (card_name);
+                        card_name = NULL;
+                        g_object_unref (card);
+
+                        should_stop = TRUE;
+                        break;
+        }
+
+out:
+        if (should_stop) {
+                GError *error;
+
+                error = g_error_new (GDM_SMARTCARD_MANAGER_ERROR,
+                                     GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+                                     "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source"));
+
+                gdm_smartcard_manager_emit_error (manager, error);
+                g_error_free (error);
+                gdm_smartcard_manager_stop_now (manager);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static void
+gdm_smartcard_manager_event_processing_stopped_handler (GdmSmartcardManager *manager)
+{
+        manager->priv->smartcard_event_source = NULL;
+        gdm_smartcard_manager_stop_now (manager);
+}
+
+static gboolean
+gdm_open_pipe (gint *write_fd,
+                  gint *read_fd)
+{
+        gint pipe_fds[2] = { -1, -1 };
+
+        g_assert (write_fd != NULL);
+        g_assert (read_fd != NULL);
+
+        if (pipe (pipe_fds) < 0) {
+                return FALSE;
+        }
+
+        if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) {
+                close (pipe_fds[0]);
+                close (pipe_fds[1]);
+                return FALSE;
+        }
+
+        if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) {
+                close (pipe_fds[0]);
+                close (pipe_fds[1]);
+                return FALSE;
+        }
+
+        *read_fd = pipe_fds[0];
+        *write_fd = pipe_fds[1];
+
+        return TRUE;
+}
+
+static void
+gdm_smartcard_manager_stop_watching_for_events (GdmSmartcardManager  *manager)
+{
+        if (manager->priv->smartcard_event_source != NULL) {
+                g_source_destroy (manager->priv->smartcard_event_source);
+                manager->priv->smartcard_event_source = NULL;
+        }
+
+        if (manager->priv->worker_thread != NULL) {
+                SECMOD_CancelWait (manager->priv->module);
+                /* FIXME: The function above doesn't seem to
+                 * always wake up WaitForAnyTokenEvent
+                 */
+                exit (0);
+                g_thread_join (manager->priv->worker_thread);
+                manager->priv->worker_thread = NULL;
+        }
+}
+
+static gboolean
+sc_load_nss (GError **error)
+{
+        SECStatus status = SECSuccess;
+        static const guint32 flags =
+        NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
+        NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT |
+        NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD;
+
+        g_debug ("attempting to load NSS database '%s'",
+                  GDM_SMARTCARD_MANAGER_NSS_DB);
+
+        status = NSS_Initialize (GDM_SMARTCARD_MANAGER_NSS_DB,
+                                 "", "", SECMOD_DB, flags);
+
+        if (status != SECSuccess) {
+                gsize error_message_size;
+                char *error_message;
+
+                error_message_size = PR_GetErrorTextLength ();
+
+                if (error_message_size == 0) {
+                        g_debug ("NSS security system could not be initialized");
+                        g_set_error (error,
+                                     GDM_SMARTCARD_MANAGER_ERROR,
+                                     GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+                                     _("NSS security system could not be initialized"));
+                        goto out;
+                }
+
+                error_message = g_slice_alloc0 (error_message_size);
+                PR_GetErrorText (error_message);
+
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+                             "%s", error_message);
+                g_debug ("NSS security system could not be initialized - %s",
+                          error_message);
+
+                g_slice_free1 (error_message_size, error_message);
+
+                goto out;
+        }
+
+        g_debug ("NSS database sucessfully loaded");
+        return TRUE;
+
+out:
+        g_debug ("NSS database couldn't be sucessfully loaded");
+        return FALSE;
+}
+
+static SECMODModule *
+load_driver (char   *module_path,
+                    GError **error)
+{
+        SECMODModule *module;
+        char *module_spec;
+        gboolean module_explicitly_specified;
+
+        g_debug ("attempting to load driver...");
+
+        module = NULL;
+        module_explicitly_specified = module_path != NULL;
+        if (module_explicitly_specified) {
+                module_spec = g_strdup_printf ("library=\"%s\"", module_path);
+                g_debug ("loading smartcard driver using spec '%s'",
+                          module_spec);
+
+                module = SECMOD_LoadUserModule (module_spec,
+                                                NULL /* parent */,
+                                                FALSE /* recurse */);
+                g_free (module_spec);
+                module_spec = NULL;
+
+        } else {
+                SECMODModuleList *modules, *tmp;
+
+                modules = SECMOD_GetDefaultModuleList ();
+
+                for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+                        if (!SECMOD_HasRemovableSlots (tmp->module) ||
+                            !tmp->module->loaded)
+                                continue;
+
+                        module = SECMOD_ReferenceModule (tmp->module);
+                        break;
+                }
+
+                /* fallback to compiled in driver path
+                 */
+                if (module == NULL) {
+                        module_path = GDM_SMARTCARD_MANAGER_DRIVER;
+                        module_spec = g_strdup_printf ("library=\"%s\"", module_path);
+                        g_debug ("loading smartcard driver using spec '%s'",
+                                module_spec);
+
+                        module = SECMOD_LoadUserModule (module_spec,
+                                NULL /* parent */,
+                                FALSE /* recurse */);
+                        g_free (module_spec);
+                        module_spec = NULL;
+                }
+
+        }
+
+        if (!module_explicitly_specified && module == NULL) {
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+                             _("no suitable smartcard driver could be found"));
+        } else if (module == NULL || !module->loaded) {
+
+                gsize error_message_size;
+                char *error_message;
+
+                if (module != NULL && !module->loaded) {
+                        g_debug ("module found but not loaded?!");
+                        SECMOD_DestroyModule (module);
+                        module = NULL;
+                }
+
+                error_message_size = PR_GetErrorTextLength ();
+
+                if (error_message_size == 0) {
+                        g_debug ("smartcard driver '%s' could not be loaded",
+                                  module_path);
+                        g_set_error (error,
+                                     GDM_SMARTCARD_MANAGER_ERROR,
+                                     GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+                                     _("smartcard driver '%s' could not be "
+                                       "loaded"), module_path);
+                        goto out;
+                }
+
+                error_message = g_slice_alloc0 (error_message_size);
+                PR_GetErrorText (error_message);
+
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+                             "%s", error_message);
+
+                g_debug ("smartcard driver '%s' could not be loaded - %s",
+                          module_path, error_message);
+                g_slice_free1 (error_message_size, error_message);
+        }
+
+out:
+        return module;
+}
+
+static void
+gdm_smartcard_manager_get_all_cards (GdmSmartcardManager *manager)
+{
+        int i;
+
+        for (i = 0; i < manager->priv->module->slotCount; i++) {
+                GdmSmartcard *card;
+                CK_SLOT_ID    slot_id;
+                gint          slot_series;
+                char *card_name;
+
+                slot_id = PK11_GetSlotID (manager->priv->module->slots[i]);
+                slot_series = PK11_GetSlotSeries (manager->priv->module->slots[i]);
+
+                card = _gdm_smartcard_new (manager->priv->module,
+                                                slot_id, slot_series);
+
+                card_name = gdm_smartcard_get_name (card);
+
+                g_hash_table_replace (manager->priv->smartcards,
+                                      card_name, card);
+        }
+}
+
+gboolean
+gdm_smartcard_manager_start (GdmSmartcardManager  *manager,
+                             GError              **error)
+{
+        GError *watching_error;
+        gint worker_fd;
+        GPid worker_pid;
+        GIOChannel *io_channel;
+        GSource *source;
+        GIOFlags channel_flags;
+        GError *nss_error;
+
+        if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED) {
+                g_debug ("smartcard manager already started");
+                return TRUE;
+        }
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTING;
+
+        worker_fd = -1;
+        worker_pid = 0;
+
+        nss_error = NULL;
+        if (!manager->priv->nss_is_loaded && !sc_load_nss (&nss_error)) {
+                g_propagate_error (error, nss_error);
+                goto out;
+        }
+        manager->priv->nss_is_loaded = TRUE;
+
+        if (manager->priv->module == NULL) {
+                manager->priv->module = load_driver (manager->priv->module_path, &nss_error);
+        }
+
+        if (manager->priv->module == NULL) {
+                g_propagate_error (error, nss_error);
+                goto out;
+        }
+
+        if (!gdm_smartcard_manager_create_worker (manager, &worker_fd, &manager->priv->worker_thread)) {
+
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+                             _("could not watch for incoming card events - %s"),
+                             g_strerror (errno));
+
+                goto out;
+        }
+
+        io_channel = g_io_channel_unix_new (worker_fd);
+
+        channel_flags = g_io_channel_get_flags (io_channel);
+        watching_error = NULL;
+
+        source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP);
+        g_io_channel_unref (io_channel);
+        io_channel = NULL;
+
+        manager->priv->smartcard_event_source = source;
+
+        g_source_set_callback (manager->priv->smartcard_event_source,
+                               (GSourceFunc) (GIOFunc)
+                               gdm_smartcard_manager_check_for_and_process_events,
+                               manager,
+                               (GDestroyNotify)
+                               gdm_smartcard_manager_event_processing_stopped_handler);
+        g_source_attach (manager->priv->smartcard_event_source, NULL);
+        g_source_unref (manager->priv->smartcard_event_source);
+
+        /* populate the hash with cards that are already inserted
+         */
+        gdm_smartcard_manager_get_all_cards (manager);
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTED;
+
+out:
+        /* don't leave it in a half started state
+         */
+        if (manager->priv->state != GDM_SMARTCARD_MANAGER_STATE_STARTED) {
+                g_debug ("smartcard manager could not be completely started");
+                gdm_smartcard_manager_stop (manager);
+        } else {
+                g_debug ("smartcard manager started");
+        }
+
+        return manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED;
+}
+
+static gboolean
+gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager)
+{
+        if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) {
+                return FALSE;
+        }
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPED;
+        gdm_smartcard_manager_stop_watching_for_events (manager);
+
+        if (manager->priv->module != NULL) {
+                SECMOD_DestroyModule (manager->priv->module);
+                manager->priv->module = NULL;
+        }
+
+        if (manager->priv->nss_is_loaded) {
+                NSS_Shutdown ();
+                manager->priv->nss_is_loaded = FALSE;
+        }
+
+        g_debug ("smartcard manager stopped");
+
+        return FALSE;
+}
+
+static void
+gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager)
+{
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPING;
+
+        g_idle_add ((GSourceFunc) gdm_smartcard_manager_stop_now, manager);
+}
+
+void
+gdm_smartcard_manager_stop (GdmSmartcardManager *manager)
+{
+        if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) {
+                return;
+        }
+
+        if (manager->priv->is_unstoppable) {
+                gdm_smartcard_manager_queue_stop (manager);
+                return;
+        }
+
+        gdm_smartcard_manager_stop_now (manager);
+}
+
+static void
+gdm_smartcard_manager_check_for_login_card (CK_SLOT_ID    slot_id,
+                                            GdmSmartcard *card,
+                                            gboolean     *is_inserted)
+{
+        g_assert (is_inserted != NULL);
+
+        if (gdm_smartcard_is_login_card (card)) {
+                *is_inserted = TRUE;
+        }
+
+}
+
+gboolean
+gdm_smartcard_manager_login_card_is_inserted (GdmSmartcardManager *manager)
+
+{
+        gboolean is_inserted;
+
+        is_inserted = FALSE;
+        g_hash_table_foreach (manager->priv->smartcards,
+                              (GHFunc)
+                              gdm_smartcard_manager_check_for_login_card,
+                              &is_inserted);
+        return is_inserted;
+}
+
+static GdmSmartcardManagerWorker *
+gdm_smartcard_manager_worker_new (gint write_fd)
+{
+        GdmSmartcardManagerWorker *worker;
+
+        worker = g_slice_new0 (GdmSmartcardManagerWorker);
+        worker->write_fd = write_fd;
+        worker->module = NULL;
+
+        worker->smartcards =
+                g_hash_table_new_full ((GHashFunc) sc_slot_id_hash,
+                                       (GEqualFunc) sc_slot_id_equal,
+                                       (GDestroyNotify) g_free,
+                                       (GDestroyNotify) g_object_unref);
+
+        return worker;
+}
+
+static void
+gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker)
+{
+        if (worker->smartcards != NULL) {
+                g_hash_table_destroy (worker->smartcards);
+                worker->smartcards = NULL;
+        }
+
+        g_slice_free (GdmSmartcardManagerWorker, worker);
+}
+
+static gboolean
+sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes)
+{
+        size_t bytes_left;
+        size_t total_bytes_read;
+        ssize_t bytes_read;
+
+        bytes_left = (size_t) num_bytes;
+        total_bytes_read = 0;
+
+        do {
+                bytes_read = read (fd, bytes + total_bytes_read, bytes_left);
+                g_assert (bytes_read <= (ssize_t) bytes_left);
+
+                if (bytes_read <= 0) {
+                        if ((bytes_read < 0) && (errno == EINTR || errno == EAGAIN)) {
+                                continue;
+                        }
+
+                        bytes_left = 0;
+                } else {
+                        bytes_left -= bytes_read;
+                        total_bytes_read += bytes_read;
+                }
+        } while (bytes_left > 0);
+
+        if (total_bytes_read <  (size_t) num_bytes) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes)
+{
+        size_t bytes_left;
+        size_t total_bytes_written;
+        ssize_t bytes_written;
+
+        bytes_left = (size_t) num_bytes;
+        total_bytes_written = 0;
+
+        do {
+                bytes_written = write (fd, bytes + total_bytes_written, bytes_left);
+                g_assert (bytes_written <= (ssize_t) bytes_left);
+
+                if (bytes_written <= 0) {
+                        if ((bytes_written < 0) && (errno == EINTR || errno == EAGAIN)) {
+                                continue;
+                        }
+
+                        bytes_left = 0;
+                } else {
+                        bytes_left -= bytes_written;
+                        total_bytes_written += bytes_written;
+                }
+        } while (bytes_left > 0);
+
+        if (total_bytes_written <  (size_t) num_bytes) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static GdmSmartcard *
+sc_read_smartcard (gint          fd,
+                   SECMODModule *module)
+{
+        GdmSmartcard *card;
+        char *card_name;
+        gsize card_name_size;
+
+        card_name_size = 0;
+        if (!sc_read_bytes (fd, &card_name_size, sizeof (card_name_size))) {
+                return NULL;
+        }
+
+        card_name = g_slice_alloc0 (card_name_size);
+        if (!sc_read_bytes (fd, card_name, card_name_size)) {
+                g_slice_free1 (card_name_size, card_name);
+                return NULL;
+        }
+        card = _gdm_smartcard_new_from_name (module, card_name);
+        g_slice_free1 (card_name_size, card_name);
+
+        return card;
+}
+
+static gboolean
+sc_write_smartcard (gint          fd,
+                    GdmSmartcard *card)
+{
+        gsize card_name_size;
+        char *card_name;
+
+        card_name = gdm_smartcard_get_name (card);
+        card_name_size = strlen (card_name) + 1;
+
+        if (!sc_write_bytes (fd, &card_name_size, sizeof (card_name_size))) {
+                g_free (card_name);
+                return FALSE;
+        }
+
+        if (!sc_write_bytes (fd, card_name, card_name_size)) {
+                g_free (card_name);
+                return FALSE;
+        }
+        g_free (card_name);
+
+        return TRUE;
+}
+
+static gboolean
+gdm_smartcard_manager_worker_emit_smartcard_removed (GdmSmartcardManagerWorker  *worker,
+                                                     GdmSmartcard               *card,
+                                                     GError                    **error)
+{
+        g_debug ("card '%s' removed!", gdm_smartcard_get_name (card));
+
+        if (!sc_write_bytes (worker->write_fd, "R", 1)) {
+                goto error_out;
+        }
+
+        if (!sc_write_smartcard (worker->write_fd, card)) {
+                goto error_out;
+        }
+
+        return TRUE;
+
+error_out:
+        g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR,
+                     GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS,
+                     "%s", g_strerror (errno));
+        return FALSE;
+}
+
+static gboolean
+gdm_smartcard_manager_worker_emit_smartcard_inserted (GdmSmartcardManagerWorker  *worker,
+                                                      GdmSmartcard               *card,
+                                                      GError                    **error)
+{
+        GError *write_error;
+
+        write_error = NULL;
+        g_debug ("card '%s' inserted!", gdm_smartcard_get_name (card));
+        if (!sc_write_bytes (worker->write_fd, "I", 1)) {
+                goto error_out;
+        }
+
+        if (!sc_write_smartcard (worker->write_fd, card)) {
+                goto error_out;
+        }
+
+        return TRUE;
+
+error_out:
+        g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR,
+                     GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS,
+                     "%s", g_strerror (errno));
+        return FALSE;
+}
+
+static gboolean
+gdm_smartcard_manager_worker_watch_for_and_process_event (GdmSmartcardManagerWorker  *worker,
+                                                          GError                    **error)
+{
+        PK11SlotInfo *slot;
+        CK_SLOT_ID slot_id, *key;
+        gint slot_series, card_slot_series;
+        GdmSmartcard *card;
+        GError *processing_error;
+
+        g_debug ("waiting for card event");
+
+        /* FIXME: we return FALSE quite a bit in this function without cleaning up
+         * resources.  By returning FALSE we're going to ultimately exit anyway, but
+         * we should still be tidier about things.
+         */
+
+        slot = SECMOD_WaitForAnyTokenEvent (worker->module, 0, PR_SecondsToInterval (1));
+        processing_error = NULL;
+
+        if (slot == NULL) {
+                int error_code;
+
+                error_code = PORT_GetError ();
+                if ((error_code == 0) || (error_code == SEC_ERROR_NO_EVENT)) {
+                        g_debug ("spurrious event occurred");
+                        return TRUE;
+                }
+
+                /* FIXME: is there a function to convert from a PORT error
+                 * code to a translated string?
+                 */
+                g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+                             _("encountered unexpected error while "
+                               "waiting for smartcard events"));
+                return FALSE;
+        }
+
+        /* the slot id and series together uniquely identify a card.
+         * You can never have two cards with the same slot id at the
+         * same time, however (I think), so we can key off of it.
+         */
+        slot_id = PK11_GetSlotID (slot);
+        slot_series = PK11_GetSlotSeries (slot);
+
+        /* First check to see if there is a card that we're currently
+         * tracking in the slot.
+         */
+        key = g_new (CK_SLOT_ID, 1);
+        *key = slot_id;
+        card = g_hash_table_lookup (worker->smartcards, key);
+
+        if (card != NULL) {
+                card_slot_series = gdm_smartcard_get_slot_series (card);
+        } else {
+                card_slot_series = -1;
+        }
+
+        if (PK11_IsPresent (slot)) {
+                /* Now, check to see if their is a new card in the slot.
+                 * If there was a different card in the slot now than
+                 * there was before, then we need to emit a removed signal
+                 * for the old card (we don't want unpaired insertion events).
+                 */
+                if ((card != NULL) &&
+                    card_slot_series != slot_series) {
+                        if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) {
+                                g_propagate_error (error, processing_error);
+                                return FALSE;
+                        }
+                }
+
+                card = _gdm_smartcard_new (worker->module,
+                                           slot_id, slot_series);
+
+                g_hash_table_replace (worker->smartcards,
+                                      key, card);
+                key = NULL;
+
+                if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) {
+                        g_propagate_error (error, processing_error);
+                        return FALSE;
+                }
+        } else {
+                /* if we aren't tracking the card, just discard the event.
+                 * We don't want unpaired remove events.  Note on startup
+                 * NSS will generate an "insertion" event if a card is
+                 * already inserted in the slot.
+                 */
+                if ((card != NULL)) {
+                        /* FIXME: i'm not sure about this code.  Maybe we
+                         * shouldn't do this at all, or maybe we should do it
+                         * n times (where n = slot_series - card_slot_series + 1)
+                         *
+                         * Right now, i'm just doing it once.
+                         */
+                        if ((slot_series - card_slot_series) > 1) {
+
+                                if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) {
+                                        g_propagate_error (error, processing_error);
+                                        return FALSE;
+                                }
+                                g_hash_table_remove (worker->smartcards, key);
+
+                                card = _gdm_smartcard_new (worker->module,
+                                                                slot_id, slot_series);
+                                g_hash_table_replace (worker->smartcards,
+                                                      key, card);
+                                key = NULL;
+                                if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) {
+                                        g_propagate_error (error, processing_error);
+                                        return FALSE;
+                                }
+                        }
+
+                        if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) {
+                                g_propagate_error (error, processing_error);
+                                return FALSE;
+                        }
+
+                        g_hash_table_remove (worker->smartcards, key);
+                        card = NULL;
+                } else {
+                        g_debug ("got spurious remove event");
+                }
+        }
+
+        g_free (key);
+        PK11_FreeSlot (slot);
+
+        return TRUE;
+}
+
+static void
+gdm_smartcard_manager_worker_run (GdmSmartcardManagerWorker *worker)
+{
+        GError *error;
+
+
+        error = NULL;
+
+        while (gdm_smartcard_manager_worker_watch_for_and_process_event (worker, &error));
+
+        if (error != NULL)  {
+                g_debug ("could not process card event - %s", error->message);
+                g_error_free (error);
+        }
+
+        gdm_smartcard_manager_worker_free (worker);
+}
+
+static gboolean
+gdm_smartcard_manager_create_worker (GdmSmartcardManager  *manager,
+                                     gint                 *worker_fd,
+                                     GThread             **worker_thread)
+{
+        GdmSmartcardManagerWorker *worker;
+        gint write_fd, read_fd;
+
+        write_fd = -1;
+        read_fd = -1;
+        if (!gdm_open_pipe (&write_fd, &read_fd)) {
+                return FALSE;
+        }
+
+        worker = gdm_smartcard_manager_worker_new (write_fd);
+        worker->module = manager->priv->module;
+
+        *worker_thread = g_thread_create ((GThreadFunc)
+                                          gdm_smartcard_manager_worker_run,
+                                          worker, TRUE, NULL);
+
+        if (*worker_thread == NULL) {
+                gdm_smartcard_manager_worker_free (worker);
+                return FALSE;
+        }
+
+        if (worker_fd) {
+                *worker_fd = read_fd;
+        }
+
+        return TRUE;
+}
+
+#ifdef GDM_SMARTCARD_MANAGER_ENABLE_TEST
+#include <glib.h>
+
+static GMainLoop *event_loop;
+static gboolean should_exit_on_next_remove = FALSE;
+
+static gboolean
+on_timeout (GdmSmartcardManager *manager)
+{
+        GError *error;
+        g_print ("Re-enabling manager.\n");
+
+        if (!gdm_smartcard_manager_start (manager, &error)) {
+                g_warning ("could not start smartcard manager - %s",
+                           error->message);
+                g_error_free (error);
+                return 1;
+        }
+        g_print ("Please re-insert smartcard\n");
+
+        should_exit_on_next_remove = TRUE;
+
+        return FALSE;
+}
+
+static void
+on_device_inserted (GdmSmartcardManager *manager,
+                    GdmSmartcard        *card)
+{
+        g_print ("smartcard inserted!\n");
+        g_print ("Please remove it.\n");
+}
+
+static void
+on_device_removed (GdmSmartcardManager *manager,
+                   GdmSmartcard        *card)
+{
+        g_print ("smartcard removed!\n");
+
+        if (should_exit_on_next_remove) {
+                g_main_loop_quit (event_loop);
+        } else {
+                g_print ("disabling manager for 2 seconds\n");
+                gdm_smartcard_manager_stop (manager);
+                g_timeout_add (2000, (GSourceFunc) on_timeout, manager);
+        }
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+        GdmSmartcardManager *manager;
+        GError *error;
+
+        g_log_set_always_fatal (G_LOG_LEVEL_ERROR
+                                | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+
+        g_type_init ();
+
+        g_message ("creating instance of 'smartcard manager' object...");
+        manager = gdm_smartcard_manager_new (NULL);
+        g_message ("'smartcard manager' object created successfully");
+
+        g_signal_connect (manager, "smartcard-inserted",
+                          G_CALLBACK (on_device_inserted), NULL);
+
+        g_signal_connect (manager, "smartcard-removed",
+                          G_CALLBACK (on_device_removed), NULL);
+
+        g_message ("starting listener...");
+
+        error = NULL;
+        if (!gdm_smartcard_manager_start (manager, &error)) {
+                g_warning ("could not start smartcard manager - %s",
+                           error->message);
+                g_error_free (error);
+                return 1;
+        }
+
+        event_loop = g_main_loop_new (NULL, FALSE);
+        g_main_loop_run (event_loop);
+        g_main_loop_unref (event_loop);
+        event_loop = NULL;
+
+        g_message ("destroying previously created 'smartcard manager' object...");
+        g_object_unref (manager);
+        manager = NULL;
+        g_message ("'smartcard manager' object destroyed successfully");
+
+        return 0;
+}
+#endif
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h
new file mode 100644
index 0000000..932a703
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h
@@ -0,0 +1,86 @@
+/* gdm-smartcard-manager.h - object for monitoring smartcard insertion and
+ *                           removal events
+ *
+ * Copyright (C) 2006, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by: Ray Strode
+ */
+#ifndef GDM_SMARTCARD_MANAGER_H
+#define GDM_SMARTCARD_MANAGER_H
+
+#define GDM_SMARTCARD_ENABLE_INTERNAL_API
+#include "gdm-smartcard.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define GDM_TYPE_SMARTCARD_MANAGER            (gdm_smartcard_manager_get_type ())
+#define GDM_SMARTCARD_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManager))
+#define GDM_SMARTCARD_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass))
+#define GDM_IS_SMARTCARD_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SMARTCARD_MANAGER))
+#define GDM_IS_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SMARTCARD_MANAGER))
+#define GDM_SMARTCARD_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass))
+#define GDM_SMARTCARD_MANAGER_ERROR           (gdm_smartcard_manager_error_quark ())
+typedef struct _GdmSmartcardManager GdmSmartcardManager;
+typedef struct _GdmSmartcardManagerClass GdmSmartcardManagerClass;
+typedef struct _GdmSmartcardManagerPrivate GdmSmartcardManagerPrivate;
+typedef enum _GdmSmartcardManagerError GdmSmartcardManagerError;
+
+struct _GdmSmartcardManager {
+    GObject parent;
+
+    /*< private > */
+    GdmSmartcardManagerPrivate *priv;
+};
+
+struct _GdmSmartcardManagerClass {
+        GObjectClass parent_class;
+
+        /* Signals */
+        void (*smartcard_inserted) (GdmSmartcardManager *manager,
+                                    GdmSmartcard        *token);
+        void (*smartcard_removed) (GdmSmartcardManager *manager,
+                                   GdmSmartcard        *token);
+        void (*error) (GdmSmartcardManager *manager,
+                       GError              *error);
+};
+
+enum _GdmSmartcardManagerError {
+    GDM_SMARTCARD_MANAGER_ERROR_GENERIC = 0,
+    GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+    GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+    GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+    GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS
+};
+
+GType gdm_smartcard_manager_get_type (void) G_GNUC_CONST;
+GQuark gdm_smartcard_manager_error_quark (void) G_GNUC_CONST;
+
+GdmSmartcardManager *gdm_smartcard_manager_new (const char *module);
+
+gboolean gdm_smartcard_manager_start (GdmSmartcardManager  *manager,
+                                      GError              **error);
+
+void gdm_smartcard_manager_stop (GdmSmartcardManager *manager);
+
+char *gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager);
+gboolean gdm_smartcard_manager_login_token_is_inserted (GdmSmartcardManager *manager);
+
+G_END_DECLS
+#endif                                /* GDM_SMARTCARD_MANAGER_H */
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c
new file mode 100644
index 0000000..75e4f33
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c
@@ -0,0 +1,167 @@
+#include "config.h"
+
+#include <fcntl.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "gdm-smartcard-manager.h"
+#include "gdm-smartcard.h"
+
+#ifndef GDM_SMARTCARDS_CONF
+#define GDM_SMARTCARDS_CONF GDMCONFDIR "/smartcards.conf"
+#endif
+
+#ifndef GDM_SMARTCARDS_GROUP
+#define GDM_SMARTCARDS_GROUP "Smartcards"
+#endif
+
+#ifndef GDM_SMARTCARDS_KEY_ENABLED
+#define GDM_SMARTCARDS_KEY_ENABLED "Enabled"
+#endif
+
+#ifndef GDM_SMARTCARDS_KEY_DRIVER
+#define GDM_SMARTCARDS_KEY_DRIVER "Driver"
+#endif
+
+static GMainLoop *event_loop;
+static GdmSmartcardManager *manager;
+static int signal_pipe_fds[2] = { -1, -1 };
+
+static void
+on_smartcard_event (const char *event_string)
+{
+        g_debug ("smartcard event '%s' happened", event_string);
+        g_print ("%s", event_string);
+        fflush (stdout);
+}
+
+static void
+watch_for_smartcards (void)
+{
+        GError *error;
+        char *driver;
+        GKeyFile *cfg;
+
+        cfg = g_key_file_new ();
+
+        error = NULL;
+        driver = NULL;
+        if (g_key_file_load_from_file (cfg, GDM_SMARTCARDS_CONF, G_KEY_FILE_NONE, &error)) {
+                if (!g_key_file_get_boolean (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_ENABLED, &error)) {
+                        g_debug ("smartcard support is not enabled");
+                        goto out;
+                }
+
+                driver = g_key_file_get_string (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_DRIVER, NULL);
+                g_debug ("smartcards driver is set to '%s'",
+                        driver == NULL || driver[0] == '\0'? "<automatic>" : driver);
+        }
+
+        g_debug ("watching for smartcard insertion and removal events");
+        manager = gdm_smartcard_manager_new (driver);
+        g_free (driver);
+
+        g_signal_connect_swapped (manager,
+                                  "smartcard-inserted",
+                                  G_CALLBACK (on_smartcard_event),
+                                  "I");
+
+        g_signal_connect_swapped (manager,
+                                  "smartcard-removed",
+                                  G_CALLBACK (on_smartcard_event),
+                                  "R");
+
+        error = NULL;
+        if (!gdm_smartcard_manager_start (manager, &error)) {
+            g_object_unref (manager);
+            manager = NULL;
+
+            if (error != NULL) {
+                    g_debug ("%s", error->message);
+                    g_error_free (error);
+            } else {
+                    g_debug ("could not start smartcard manager");
+
+            }
+            goto out;
+        }
+out:
+        g_key_file_free (cfg);
+}
+
+static void
+stop_watching_for_smartcards (void)
+{
+        if (manager != NULL) {
+                gdm_smartcard_manager_stop (manager);
+                g_object_unref (manager);
+                manager = NULL;
+        }
+}
+
+static void
+on_term_signal (int signal_number)
+{
+        close (signal_pipe_fds[1]);
+        signal_pipe_fds[1] = -1;
+}
+
+static gboolean
+after_term_signal (GIOChannel   *io_channel,
+                   GIOCondition  condition,
+                   gpointer      data)
+{
+        g_main_loop_quit (event_loop);
+        return FALSE;
+}
+
+static void
+on_debug_message (const char     *log_domain,
+                  GLogLevelFlags  log_level,
+                  const char     *message,
+                  gpointer        user_data)
+{
+        g_printerr ("*** DEBUG: %s\n", message);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+        GIOChannel *io_channel;
+
+        setlocale (LC_ALL, "");
+
+        g_type_init ();
+
+        g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, on_debug_message, NULL);
+
+        event_loop = g_main_loop_new (NULL, FALSE);
+
+        watch_for_smartcards ();
+
+        signal (SIGTERM, on_term_signal);
+        signal (SIGPIPE, on_term_signal);
+        if (pipe (signal_pipe_fds) != 0) {
+                return 1;
+        }
+        fcntl (signal_pipe_fds[0], F_SETFD, FD_CLOEXEC);
+        fcntl (signal_pipe_fds[1], F_SETFD, FD_CLOEXEC);
+
+        io_channel = g_io_channel_unix_new (signal_pipe_fds[0]);
+        g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL);
+        g_io_channel_set_encoding (io_channel, NULL, NULL);
+        g_io_channel_set_buffered (io_channel, FALSE);
+        g_io_add_watch (io_channel, G_IO_HUP, after_term_signal, NULL);
+        g_io_channel_set_close_on_unref (io_channel, TRUE);
+        g_io_channel_unref (io_channel);
+
+        g_main_loop_run (event_loop);
+
+        stop_watching_for_smartcards ();
+
+        return 0;
+}
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c
new file mode 100644
index 0000000..c0e2739
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c
@@ -0,0 +1,558 @@
+/* gdm-smartcard.c - smartcard object
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#define GDM_SMARTCARD_ENABLE_INTERNAL_API
+#include "gdm-smartcard.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <cert.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <prerror.h>
+#include <secmod.h>
+#include <secerr.h>
+
+struct _GdmSmartcardPrivate {
+        SECMODModule *module;
+        GdmSmartcardState state;
+
+        CK_SLOT_ID slot_id;
+        int slot_series;
+
+        PK11SlotInfo *slot;
+        char *name;
+
+        CERTCertificate *signing_certificate;
+        CERTCertificate *encryption_certificate;
+};
+
+static void gdm_smartcard_finalize (GObject *object);
+static void gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class);
+static void gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class);
+static void gdm_smartcard_set_property (GObject       *object,
+                                       guint          prop_id,
+                                       const GValue  *value,
+                                       GParamSpec    *pspec);
+static void gdm_smartcard_get_property (GObject     *object,
+                                       guint        prop_id,
+                                       GValue      *value,
+                                       GParamSpec  *pspec);
+static void gdm_smartcard_set_name (GdmSmartcard *card, const char *name);
+static void gdm_smartcard_set_slot_id (GdmSmartcard *card,
+                                      int                 slot_id);
+static void gdm_smartcard_set_slot_series (GdmSmartcard *card,
+                                          int          slot_series);
+static void gdm_smartcard_set_module (GdmSmartcard *card,
+                                     SECMODModule *module);
+
+static PK11SlotInfo *gdm_smartcard_find_slot_from_id (GdmSmartcard *card,
+                                                     int slot_id);
+
+static PK11SlotInfo *gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card,
+                                                            const char  *card_name);
+static gboolean gdm_smartcard_fetch_certificates (GdmSmartcard *card);
+
+#ifndef GDM_SMARTCARD_DEFAULT_SLOT_ID
+#define GDM_SMARTCARD_DEFAULT_SLOT_ID ((gulong) -1)
+#endif
+
+#ifndef GDM_SMARTCARD_DEFAULT_SLOT_SERIES
+#define GDM_SMARTCARD_DEFAULT_SLOT_SERIES -1
+#endif
+
+enum {
+        PROP_0 = 0,
+        PROP_NAME,
+        PROP_SLOT_ID,
+        PROP_SLOT_SERIES,
+        PROP_MODULE,
+        NUMBER_OF_PROPERTIES
+};
+
+enum {
+        INSERTED,
+        REMOVED,
+        NUMBER_OF_SIGNALS
+};
+
+static guint gdm_smartcard_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (GdmSmartcard, gdm_smartcard, G_TYPE_OBJECT);
+
+static void
+gdm_smartcard_class_init (GdmSmartcardClass *card_class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (card_class);
+
+        gobject_class->finalize = gdm_smartcard_finalize;
+
+        gdm_smartcard_class_install_signals (card_class);
+        gdm_smartcard_class_install_properties (card_class);
+
+        g_type_class_add_private (card_class,
+                                  sizeof (GdmSmartcardPrivate));
+}
+
+static void
+gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (card_class);
+
+        gdm_smartcard_signals[INSERTED] =
+                g_signal_new ("inserted",
+                          G_OBJECT_CLASS_TYPE (object_class),
+                          G_SIGNAL_RUN_LAST,
+                          G_STRUCT_OFFSET (GdmSmartcardClass,
+                                           inserted),
+                          NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                          G_TYPE_NONE, 0);
+
+        gdm_smartcard_signals[REMOVED] =
+                g_signal_new ("removed",
+                          G_OBJECT_CLASS_TYPE (object_class),
+                          G_SIGNAL_RUN_LAST,
+                          G_STRUCT_OFFSET (GdmSmartcardClass,
+                                           removed),
+                          NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                          G_TYPE_NONE, 0);
+}
+
+static void
+gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class)
+{
+        GObjectClass *object_class;
+        GParamSpec *param_spec;
+
+        object_class = G_OBJECT_CLASS (card_class);
+        object_class->set_property = gdm_smartcard_set_property;
+        object_class->get_property = gdm_smartcard_get_property;
+
+        param_spec = g_param_spec_ulong ("slot-id", _("Slot ID"),
+                                   _("The slot the card is in"),
+                                   1, G_MAXULONG,
+                                   GDM_SMARTCARD_DEFAULT_SLOT_ID,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_SLOT_ID, param_spec);
+
+        param_spec = g_param_spec_int ("slot-series", _("Slot Series"),
+                                   _("per-slot card identifier"),
+                                   -1, G_MAXINT,
+                                   GDM_SMARTCARD_DEFAULT_SLOT_SERIES,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_SLOT_SERIES, param_spec);
+
+        param_spec = g_param_spec_string ("name", _("name"),
+                                      _("name"), NULL,
+                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_NAME, param_spec);
+
+        param_spec = g_param_spec_pointer ("module", _("Module"),
+                                       _("smartcard driver"),
+                                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_MODULE, param_spec);
+}
+
+static void
+gdm_smartcard_set_property (GObject       *object,
+                            guint          prop_id,
+                            const GValue  *value,
+                            GParamSpec    *pspec)
+{
+        GdmSmartcard *card = GDM_SMARTCARD (object);
+
+        switch (prop_id) {
+                case PROP_NAME:
+                        gdm_smartcard_set_name (card, g_value_get_string (value));
+                        break;
+
+                case PROP_SLOT_ID:
+                        gdm_smartcard_set_slot_id (card,
+                                                   g_value_get_ulong (value));
+                        break;
+
+                case PROP_SLOT_SERIES:
+                        gdm_smartcard_set_slot_series (card,
+                                                       g_value_get_int (value));
+                        break;
+
+                case PROP_MODULE:
+                        gdm_smartcard_set_module (card,
+                                                  (SECMODModule *)
+                                                  g_value_get_pointer (value));
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+CK_SLOT_ID
+gdm_smartcard_get_slot_id (GdmSmartcard *card)
+{
+        return card->priv->slot_id;
+}
+
+GdmSmartcardState
+gdm_smartcard_get_state (GdmSmartcard *card)
+{
+        return card->priv->state;
+}
+
+char *
+gdm_smartcard_get_name (GdmSmartcard *card)
+{
+        return g_strdup (card->priv->name);
+}
+
+gboolean
+gdm_smartcard_is_login_card (GdmSmartcard *card)
+{
+        const char *login_card_name;
+        login_card_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME");
+
+        if ((login_card_name == NULL) || (card->priv->name == NULL)) {
+                return FALSE;
+        }
+
+        if (strcmp (card->priv->name, login_card_name) == 0) {
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+gdm_smartcard_get_property (GObject    *object,
+                            guint        prop_id,
+                            GValue      *value,
+                            GParamSpec  *pspec)
+{
+        GdmSmartcard *card = GDM_SMARTCARD (object);
+
+        switch (prop_id) {
+                case PROP_NAME:
+                        g_value_take_string (value,
+                                             gdm_smartcard_get_name (card));
+                        break;
+
+                case PROP_SLOT_ID:
+                        g_value_set_ulong (value,
+                                           (gulong) gdm_smartcard_get_slot_id (card));
+                        break;
+
+                case PROP_SLOT_SERIES:
+                        g_value_set_int (value,
+                                         gdm_smartcard_get_slot_series (card));
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+static void
+gdm_smartcard_set_name (GdmSmartcard *card,
+                        const char   *name)
+{
+        if (name == NULL) {
+                return;
+        }
+
+        if ((card->priv->name == NULL) ||
+            (strcmp (card->priv->name, name) != 0)) {
+                g_free (card->priv->name);
+                card->priv->name = g_strdup (name);
+
+                if (card->priv->slot == NULL) {
+                        card->priv->slot = gdm_smartcard_find_slot_from_card_name (card,
+                                                                                     card->priv->name);
+
+                        if (card->priv->slot != NULL) {
+                                int slot_id, slot_series;
+
+                                slot_id = PK11_GetSlotID (card->priv->slot);
+                                if (slot_id != card->priv->slot_id) {
+                                        gdm_smartcard_set_slot_id (card, slot_id);
+                                }
+
+                                slot_series = PK11_GetSlotSeries (card->priv->slot);
+                                if (slot_series != card->priv->slot_series) {
+                                        gdm_smartcard_set_slot_series (card, slot_series);
+                                }
+
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED);
+                        } else {
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED);
+                        }
+                }
+
+                g_object_notify (G_OBJECT (card), "name");
+        }
+}
+
+static void
+gdm_smartcard_set_slot_id (GdmSmartcard *card,
+                           int           slot_id)
+{
+        if (card->priv->slot_id != slot_id) {
+                card->priv->slot_id = slot_id;
+
+                if (card->priv->slot == NULL) {
+                        card->priv->slot = gdm_smartcard_find_slot_from_id (card,
+                                                                             card->priv->slot_id);
+
+                        if (card->priv->slot != NULL) {
+                                const char *card_name;
+
+                                card_name = PK11_GetTokenName (card->priv->slot);
+                                if ((card->priv->name == NULL) ||
+                                    ((card_name != NULL) &&
+                                    (strcmp (card_name, card->priv->name) != 0))) {
+                                        gdm_smartcard_set_name (card, card_name);
+                                }
+
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED);
+                        } else {
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED);
+                        }
+                }
+
+                g_object_notify (G_OBJECT (card), "slot-id");
+        }
+}
+
+static void
+gdm_smartcard_set_slot_series (GdmSmartcard *card,
+                               int           slot_series)
+{
+        if (card->priv->slot_series != slot_series) {
+                card->priv->slot_series = slot_series;
+                g_object_notify (G_OBJECT (card), "slot-series");
+        }
+}
+
+static void
+gdm_smartcard_set_module (GdmSmartcard *card,
+                          SECMODModule *module)
+{
+        gboolean should_notify;
+
+        if (card->priv->module != module) {
+                should_notify = TRUE;
+        } else {
+                should_notify = FALSE;
+        }
+
+        if (card->priv->module != NULL) {
+                SECMOD_DestroyModule (card->priv->module);
+                card->priv->module = NULL;
+        }
+
+        if (module != NULL) {
+                card->priv->module = SECMOD_ReferenceModule (module);
+        }
+
+        if (should_notify) {
+                g_object_notify (G_OBJECT (card), "module");
+        }
+}
+
+int
+gdm_smartcard_get_slot_series (GdmSmartcard *card)
+{
+        return card->priv->slot_series;
+}
+
+static void
+gdm_smartcard_init (GdmSmartcard *card)
+{
+
+        g_debug ("initializing smartcard ");
+
+        card->priv = G_TYPE_INSTANCE_GET_PRIVATE (card,
+                                                  GDM_TYPE_SMARTCARD,
+                                                  GdmSmartcardPrivate);
+
+        if (card->priv->slot != NULL) {
+                card->priv->name = g_strdup (PK11_GetTokenName (card->priv->slot));
+        }
+}
+
+static void gdm_smartcard_finalize (GObject *object)
+{
+        GdmSmartcard *card;
+        GObjectClass *gobject_class;
+
+        card = GDM_SMARTCARD (object);
+
+        g_free (card->priv->name);
+
+        gdm_smartcard_set_module (card, NULL);
+
+        gobject_class = G_OBJECT_CLASS (gdm_smartcard_parent_class);
+
+        gobject_class->finalize (object);
+}
+
+GQuark gdm_smartcard_error_quark (void)
+{
+        static GQuark error_quark = 0;
+
+        if (error_quark == 0) {
+                error_quark = g_quark_from_static_string ("gdm-smartcard-error-quark");
+        }
+
+        return error_quark;
+}
+
+GdmSmartcard *
+_gdm_smartcard_new (SECMODModule *module,
+                    CK_SLOT_ID    slot_id,
+                    int           slot_series)
+{
+        GdmSmartcard *card;
+
+        g_return_val_if_fail (module != NULL, NULL);
+        g_return_val_if_fail (slot_id >= 1, NULL);
+        g_return_val_if_fail (slot_series > 0, NULL);
+        g_return_val_if_fail (sizeof (gulong) == sizeof (slot_id), NULL);
+
+        card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD,
+                                             "module", module,
+                                             "slot-id", (gulong) slot_id,
+                                             "slot-series", slot_series,
+                                             NULL));
+        return card;
+}
+
+GdmSmartcard *
+_gdm_smartcard_new_from_name (SECMODModule *module,
+                              const char   *name)
+{
+        GdmSmartcard *card;
+
+        g_return_val_if_fail (module != NULL, NULL);
+        g_return_val_if_fail (name != NULL, NULL);
+
+        card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD,
+                                            "module", module,
+                                            "name", name,
+                                            NULL));
+        return card;
+}
+
+void
+_gdm_smartcard_set_state (GdmSmartcard      *card,
+                          GdmSmartcardState  state)
+{
+        /* gdm_smartcard_fetch_certificates (card); */
+        if (card->priv->state != state) {
+                card->priv->state = state;
+
+                if (state == GDM_SMARTCARD_STATE_INSERTED) {
+                        g_signal_emit (card, gdm_smartcard_signals[INSERTED], 0);
+                } else if (state == GDM_SMARTCARD_STATE_REMOVED) {
+                        g_signal_emit (card, gdm_smartcard_signals[REMOVED], 0);
+                } else {
+                        g_assert_not_reached ();
+                }
+        }
+}
+
+/* So we could conceivably make the closure data a pointer to the card
+ * or something similiar and then emit signals when we want passwords,
+ * but it's probably easier to just get the password up front and use
+ * it.  So we just take the passed in g_malloc'd (well probably, who knows)
+ * and strdup it using NSPR's memory allocation routines.
+ */
+static char *
+gdm_smartcard_password_handler (PK11SlotInfo *slot,
+                                PRBool        is_retrying,
+                                const char   *password)
+{
+        if (is_retrying) {
+                return NULL;
+        }
+
+        return password != NULL? PL_strdup (password): NULL;
+}
+
+gboolean
+gdm_smartcard_unlock (GdmSmartcard *card,
+                      const char   *password)
+{
+        SECStatus status;
+
+        PK11_SetPasswordFunc ((PK11PasswordFunc) gdm_smartcard_password_handler);
+
+        /* we pass PR_TRUE to load certificates
+        */
+        status = PK11_Authenticate (card->priv->slot, PR_TRUE, (gpointer) password);
+
+        if (status != SECSuccess) {
+                g_debug ("could not unlock card - %d", status);
+                return FALSE;
+        }
+        return TRUE;
+}
+
+static PK11SlotInfo *
+gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card,
+                                        const char   *card_name)
+{
+        int i;
+
+        for (i = 0; i < card->priv->module->slotCount; i++) {
+                const char *slot_card_name;
+
+                slot_card_name = PK11_GetTokenName (card->priv->module->slots[i]);
+
+                if ((slot_card_name != NULL) &&
+                    (strcmp (slot_card_name, card_name) == 0)) {
+                        return card->priv->module->slots[i];
+                }
+        }
+
+        return NULL;
+}
+
+static PK11SlotInfo *
+gdm_smartcard_find_slot_from_id (GdmSmartcard *card,
+                                 int           slot_id)
+{
+        int i;
+
+        for (i = 0; i < card->priv->module->slotCount; i++) {
+                if (PK11_GetSlotID (card->priv->module->slots[i]) == slot_id) {
+                        return card->priv->module->slots[i];
+                }
+        }
+
+        return NULL;
+}
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h b/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h
new file mode 100644
index 0000000..20303bd
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h
@@ -0,0 +1,94 @@
+/* securitycard.h - api for reading and writing data to a security card
+ *
+ * Copyright (C) 2006 Ray Strode
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef GDM_SMARTCARD_H
+#define GDM_SMARTCARD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <secmod.h>
+
+G_BEGIN_DECLS
+#define GDM_TYPE_SMARTCARD            (gdm_smartcard_get_type ())
+#define GDM_SMARTCARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD, GdmSmartcard))
+#define GDM_SMARTCARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD, GdmSmartcardClass))
+#define GDM_IS_SMARTCARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD))
+#define GDM_IS_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD))
+#define GDM_SMARTCARD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD, GdmSmartcardClass))
+#define GDM_SMARTCARD_ERROR           (gdm_smartcard_error_quark ())
+typedef struct _GdmSmartcardClass GdmSmartcardClass;
+typedef struct _GdmSmartcard GdmSmartcard;
+typedef struct _GdmSmartcardPrivate GdmSmartcardPrivate;
+typedef enum _GdmSmartcardError GdmSmartcardError;
+typedef enum _GdmSmartcardState GdmSmartcardState;
+
+typedef struct _GdmSmartcardRequest GdmSmartcardRequest;
+
+struct _GdmSmartcard {
+    GObject parent;
+
+    /*< private > */
+    GdmSmartcardPrivate *priv;
+};
+
+struct _GdmSmartcardClass {
+    GObjectClass parent_class;
+
+    void (* inserted) (GdmSmartcard *card);
+    void (* removed)  (GdmSmartcard *card);
+};
+
+enum _GdmSmartcardError {
+    GDM_SMARTCARD_ERROR_GENERIC = 0,
+};
+
+enum _GdmSmartcardState {
+    GDM_SMARTCARD_STATE_INSERTED = 0,
+    GDM_SMARTCARD_STATE_REMOVED,
+};
+
+GType gdm_smartcard_get_type (void) G_GNUC_CONST;
+GQuark gdm_smartcard_error_quark (void) G_GNUC_CONST;
+
+CK_SLOT_ID gdm_smartcard_get_slot_id (GdmSmartcard *card);
+gint gdm_smartcard_get_slot_series (GdmSmartcard *card);
+GdmSmartcardState gdm_smartcard_get_state (GdmSmartcard *card);
+
+char *gdm_smartcard_get_name (GdmSmartcard *card);
+gboolean gdm_smartcard_is_login_card (GdmSmartcard *card);
+
+gboolean gdm_smartcard_unlock (GdmSmartcard *card,
+                               const char   *password);
+
+/* don't under any circumstances call these functions */
+#ifdef GDM_SMARTCARD_ENABLE_INTERNAL_API
+
+GdmSmartcard *_gdm_smartcard_new (SECMODModule *module,
+                                  CK_SLOT_ID    slot_id,
+                                  gint          slot_series);
+GdmSmartcard *_gdm_smartcard_new_from_name (SECMODModule *module,
+                                            const char   *name);
+
+void _gdm_smartcard_set_state (GdmSmartcard      *card,
+                               GdmSmartcardState  state);
+#endif
+
+G_END_DECLS
+#endif                                /* GDM_SMARTCARD_H */
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam b/gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam
new file mode 100644
index 0000000..3c803f7
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam
@@ -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 --git a/gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile.am b/gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile.am
new file mode 100644
index 0000000..661d687
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile.am
@@ -0,0 +1,5 @@
+iconsdir = $(datadir)/icons/hicolor/16x16/apps
+
+icons_DATA = gdm-smartcard.png
+
+EXTRA_DIST = $(icons_DATA)
diff --git a/gui/simple-greeter/plugins/smartcard/icons/16x16/gdm-smartcard.png b/gui/simple-greeter/plugins/smartcard/icons/16x16/gdm-smartcard.png
new file mode 100644
index 0000000000000000000000000000000000000000..0112af1b8d891ad312f9fcc3bf6074df3e003359
GIT binary patch
literal 871
zcmV-t1DO1YP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXJ-
z6cQQvze?o*000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0008QNkl<Z
zILoDzO=uKn0EVCWc6N4VH=}F?g{J6QBie2n4H`-{iH3Ocr_xLD)X+mq4R}|3XeEcG
zP)QFBf>25h)mRS()F5IML|LeofZ5$Phq#SvEXn3)x9QHz&V1jD*OHUa=lnh|yoWc&
zo1+2m0}628RN4)kP9zfJhGEoz$TZEX{|)Htgf`%?ZQIABl!txa-_X&~;rI9Vo88^r
z<nwt(Mn*QTFF2Y?r9O+I=!oZe>1;L|^!4>6dV71>-}NC2_0X(TUE+qIrDYmT)AaSb
zqQSwzu&1X-?Cbi7rG`eaTxYT75y*%ZE6&r0<4pW|3Ey2K2m+~;x&Y(=rCFBMd{LP<
ze!f%2@g%ktXZzbJvY9kitjV=&V+;=uGc+_rtyZH_sW3S?$@KK}cfeU)DJ6Ufjwku*
z#Jf~p{6)1o%ag||*tX5+=qR~dj#8<_+}s>O2(sBM#bWVH7>3^p-}m(}(unH<C4H1q
zWHK4r+uJD>3ls_kY}+QE&oeeQMlzYCTCI{!r;|XJ05A-lFao6%Gcz-k%VnBP$+`1i
z^JL;6j^og}#l)>&;?s|J5k(Q5do}@lp##MA7)pgK{$tR(^AIgbgR>VO(YA9dDYL-`
zsh`;Y<p!MD+jMMP<A+aP@psMP-hFxnfaf{@cumPKH~-|1=PR84yb~qfLZPv}e}LA#
z@N(%lawET!GFCAI7oZ{l5=IPwdsFjl+mU9z?h*tcD+_Z3fh0x)fUDyUsu2TS0~G-v
z0AfPp_`wW&+tPGyUS#>%6?x&ziM4udm5K2(B3uPvQ%dL5Nt5QX;$Nu&Y|(&dU|=Ac
znwoM8g@R?8=8}~1uJ8MIfoDQ+^ZMlj@4UAwMAl}h&MMZb@Tk<}`x}kRnq^tHG);3I
x$GHPM0G_Y=$2Q;_pbrQE4|oMk0zdM9c?}H%H!P_~w{rjh002ovPDHLkV1ieHj1>R?

literal 0
HcmV?d00001

diff --git a/gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile.am b/gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile.am
new file mode 100644
index 0000000..e79d85b
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile.am
@@ -0,0 +1,5 @@
+iconsdir = $(datadir)/icons/hicolor/48x48/apps
+
+icons_DATA = gdm-smartcard.png
+
+EXTRA_DIST = $(icons_DATA)
diff --git a/gui/simple-greeter/plugins/smartcard/icons/48x48/gdm-smartcard.png b/gui/simple-greeter/plugins/smartcard/icons/48x48/gdm-smartcard.png
new file mode 100644
index 0000000000000000000000000000000000000000..35d5578d9ec83ae6b17aa7230c1b4df34e13e2cc
GIT binary patch
literal 4202
zcmV-w5S8zVP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp)
z=>Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXJ-
z6cal&#5zC#000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000lgNkl<Z
zXx{Cad2C$AeaAoZ-rIw_ORjkBl9DKjl6A0T?0~kcgIw8)k0g$Q#I9>2aFf(X>ozT5
zC$5a<Xi*?(0=rFf6bRrpMPt-W(?*Sv>Zk)aA|*R=Bt;5o9kwWGLxmKt<-N<j-g`6C
zKlUJ3l-;CF+!n|H4|~j;neT6Yzu))wo8Q2%+b_Mf12Mq*{{nae@VmgbOw*hMbn*G(
z|5d;(z(;`ROw+tp0vw4%+=m`|NPq2XU(=nPoeFsQ=b7y7P*W6m7jUP?<9WB^IIRHn
z_4O(iiy5(4jPCAkHg4Q#3LyYEbm)+{aN&ZXwf<ibpdPprxYO(PzT39#O#tC=SoQSu
z7(G2b^z`(wWy==BFpP?UB>{vG7>41KQobz$)Btw?d%RxnyB)`A*IJ8UFsQn_yUTz(
zIywx~G;av_`rnpip|uYDs!S+YWQjXH9?w0l>%LQKZTS6uy>sVI(cRrmPfriG+;WTI
z@p#@6@Iu;UXgp0zq>6^HuiPI91TYLkD5ZWW0{DTQz#hx8?o~?Nrj#<hUa#J^ZJX%n
z=^+-2(bd%@yk74O!RBp=X&TI>^8Cli339gNwym|C9-PB*HI@kv?`fqW>?@BILKy#D
z4S0a<MZotcrFJN#Jf>;tuC6Y+ySqg!7UTBYZx?>Q|Av62(oD=`2?xClT$$(W&^%g!
zTIQ6mo}a-m1X^pf7EI3NXk4{4pHeFHiwMvG>@`jEE+8hQ^c#kuJ3Bk+>FE)%Sd1Mz
zc8Fjw_@?GCIhSXAHcNDspBGO};7E<_NQ5YWEH$6Uz?B-Qba9Tt=f>65<vGLQFyi_J
z-a>#Ugb-f=#OBSLRV)@WcJJQJ&Ye3&I2?Xcz^>Gsxim*Q?-KBt3|v`2Duq&D7y_j<
z0x%8GT2LLZXlt%!eN&J%4OM6j-+g(6iP<cUs~DTkvZ<x!7j{Ttnr8OVM<4Y+_0&_;
z*Vn%#LyROc96vve&to#T=#aHtl+rAxYma3hl_peWvA!w9`lbMD8-w^f#)>&p8JBMy
zy3F*%&q)02ENiPXEG{l``t)fA1_rW9sr5kOEj1ve^tZRS)6mec0`UBz&A;|tBkxE8
zJ`-DNTvuTTQMPBVWuO4R$D}nHpshK?+Qt9@f2Dh6vsn^}1XEK}BoYa1+on2P$8&%6
zXIvfZ$23jq>+2Chkjv#zN>u?Pz<%H<VEj#eKq*z;9i=qKUYjD7aT%M<Vmk#Y%Q*_A
zG+s-fw7_o}tcwO|YYx%U7$6j=q|9_WO(KyXkw`E(IY~O5UhbfL|NGzP@ZrNWH#hU}
z!w<7_=T2&CYY{?_&1RXNp60}f6W(LTjy;l0Cf^G@27LF10u*TrfFJZv5+6<BNI+{0
zLtqMlX$o2*0X8&;Xo&==304ALSXe0UZX%H&o6S~ovENT55}~1?fv2B-nu7-qa`)YL
zbN~JK)6vnv{QNv~b91CpDU!(~Qp!?1;n1N&bprzf`+?sFzOh1pTrP(Yf^^R1%C$vY
zsf%U=yKj!r)*Pg^+E*T=wPtp9whVY`YKnY5UjaA}2oQ-xh(@DCqfu&VY7j#3)vtb)
z7hZUQ2OfBUM;>{Erluyw#>N;L8e(*Gv`DLkM=6Eh@8_O-?!oKzic_afJqug{j#UWY
zI1WMx{9Y4}DUeEFnSyt9HPNv?jFgh;>FJ6cc3rnJpQx!J5{Z<b>gwtW?Zo48;_*12
z``qW)zI{6nKKLN5t*wlWjnUuVPdc4OO3Cu*tCS+0PNS5ftE-FQ;bCK9V&ZR$v@chf
zkjFCFxTc!(L-W*!{dBgi;_~Io^!N9#0K96|Dx%RSjg5^&qfr8ZKzY%*xj8OgyvW4F
z1XEK}<Z`)EmI=Up_ua>>x86!T9_QS-bIi`plFQ}rcswhZN^6ajlEuYEVzC(e_wU~V
z+zY(8LIVqFhs)QJ0IY2cAcP>F&!d#0zP_GlG)g2Ap|P=%QWSPwm+9$g;_-OdoE*og
z01*m>XliQW%9Sf@-MW=syLK@>JxwZ=VrXcH^XJdAdi838!626|T_TxG(%9I@=FOYS
zb7V3ZJRT2`NQ6Wp@!QJ<P}ywO5JE6Il|gG!%wG!3bo0$O)7jZcDU-BqyKF{@M1q-_
znF_TFA*ijbE!%!cJM;7N3=R(Rfe(Cuwzf6~2M0NK?i>RH14t>!X0sePaDb|+Dtta4
zufP5}>(;HKy}g}yJWeK)K}yM*HET#D5_gq-z%<Q_5F#XmU|lqbD+|E&vS4PKCPD~K
zo;=Cm;9$k>nx;u(V<SyXO=S&vz1|h3FfuZNQi?5GwqV;fN-4(2#~B|Vr@OnG0|ySU
zckf=FfBt!%efC-Q?c0aX=VQZ$4Gawpk;~=CX0wDsq0%8~6)<lt-?^Hg4<QOkmz25!
zgb)J9ams2eF-${41D0jIX>MPRx)Uz}P+MC|E|(*h%P~DYjo0fXolcX@W|^3n;K?VS
z<jk2fg}gl&WaGw-^!E0W$z({SQY_2IYRUp6lSzaSj83NusemoZnph5K_wL;{1n4*p
zQ&UsS&CRiK<3_5gs>=PjT#i5a<Hz{MKm1EsV~*pH&*#bIa`=2cT-Rmm*3AeOxpeV|
zgaVT7x3yAN7ow%5rR<BTREkI>QW?mE5b2T$nTAnL5%hH+mw(UYawHN7CMG7BoSbBK
zcDC#XmSxf2-d=ux&mFsn4_)CepZOr~-DB`-zg6b=QkMFm4`zAa9rIv#i`l6cfsfvw
z_z8tV<+xB{xngSqhOH8x3L#k27{o9{kzJNdD3i&Q!A?v}Fh4)Pf>N8BnrLorrmd~5
zBC`MZ%vp~A^A0vdhpDPHkW!Sv=ks~A)_B7YQoHF6K%;R$=YcHQS6;()T|6F7S%CJ<
zbqo!K7#mys4zSHC0Te=DI}%+K&@JZcGcz+BJb17yxe$W-`uZ~9Xf#@3GS_Qtdshpd
zwL$D_<2Z{M=10zx+0coUlJW6zgb*Yyo<{4fMa?VFE?|@Y=}Y|0muHAXA{a&?n~etY
z9P4|4p56OfCnvLCu!Imf*L4eElX(zJ0YnHvFc_q*t*y-E(P;FBEOY(uGv_CfN@Iyd
zG=jRG)y$q8<eu>NID7Ow6H`@eTVF+6OPB|KYcpCU3TtbPMgo#YKK*`%esqSRvqNln
zpqi@2ChmQx6*WK2XMblkk3SRtJqyT}R35DhAP@)vj$3fzKp?=LJ$v31aC~^4{x=eA
zXj#R{fia%>`ybP`HcWiDU}lTM)ujQ{++^d8y6n0%1+77R?~CGDRgI<Mq~jx$c2DJP
zoW(iZ#U!D{6z#Ps94mvnkmtwGoF#gbCL(QsbC%Xx+qPX!CF*LtjL%5WOCYs|BR?8u
z&yIDNMx`KoKJuLvIeL6}o_!y?jc>ki0dHM>MW1&~E51#8(B?Z)b2G@9BjiR-q4GA`
z1ub=fK}=Ct*j6}6g`JexNsUf|*Y6{37kt7}N;%nV79j+wjLp=14y`qgT5{rlI`BH5
z{mKvd{1e@L^!^UAIhUc)1+HFO;Fhh8oWDGcl$uX|^j7xn*+4^G6}2^f9{+F`vDn{L
z20p7A?c0LsjY3r&qTw=za|xYyakFs#oWrS~*yucTzr#yQorzb0vK2C~srG4v)|3pQ
zM5AR5h>{xR(mC-vFAnngC%W-i2J7yu1oZVkzl;0s*uX+6PhHsmU!P$`9z1I?gdc^x
zt`9mkTYUR_S^n|gGW_BFK_3555VOj_sxol18q=#;KFm@|=@P?K`7AcIgt;&@%krwE
zWg5KNKgNqM4*{_6W4G~}_ipC-e}A2u*41JfVg>lwWR9!jDW>ML#nMs{q)Z@$4<V`$
zI(I`M>sA?5`wY$vF6CHez{E%@3`>BkDOHUut+h_2QsozaHc|NQ$fdzg?~8Hx=udg_
z(OcQlR#*Q2Q;*%gl=Cmz42>tbG(5-E@f1>O6q;<lG7U%P5iDRBQIuX-0e*Bs^1|U9
zA+KOzPBJsEsr7@f1eg?n%gPhkf-Xb}k#{6nyVSd+gDq`!6~F-&GY&)JNv@2{6CX=r
zI|?Ddc4Ya`Z<z*TSKj~{E5;gX3`5+Ez^Ksl=(yz0ho-3#28}_Jkk23<m)zD-$b<{U
zV1<@Tl@P1Q`5F*Dk4bZVfU)Ux!BNMOw5=}GYBD*OtK&)H*XFrAnk1KZF@zxR6a+4T
z_E-j0K8tMLWov6K+qd5i*H_8q2trf=mBrpnPJ=551q&ur8#LH-)6zRyszQZNQ4Y8k
zKq{3gFLC#_7M?r&1_0lF=`v0ARV-#4k{KJ*Fp5<QUEC3YWf}ylETVNmGC7AmU2E8|
zW~m^yf9+bH`{r@J@NplaR63Qz7=|CND}NdSLSBPvpCIHD+!gau8y2WsVLdkw%GEe>
zX-_O5v;07EtbcS_^-VG|xmachDJ7OAFfh5jGlJ`CydHyHoz3{XW<?F?`0?W$J$jV>
z{(dz$IAT2ho_d-#6|#4&hB1V1g$WsgB6x$QI*YsCwN&|#S%sTbNE@USNTtgHIC@#}
zG8FJ)ngXp0H6j3yX`s=3<h|`A<}yTT1GGe!YOl6!^U5o)(%ak1kt0Xci4!Mq9LEqs
zIKU~O?T@~cTK%mrh6&ahpdGZ%-+*EZ;6pxx_wDuL_X||6U@qx#7n#!}^MZO)VB5N~
ztWs+4?_N*9XOhiJ?%uW*LkJRcS?X$hghM`Bq9I(@<-~~-^!4@8+uN&NdF2(I&*x1c
zgfa}{jFhq$=+#;u2hzZA^q#iQ9DF(Nf3U&88w7m=23oh)<WD~n;I)&A9Xm|0HOhtg
zOE&pgNirvx&YHBY64-X(Hpl|(<pOvtgZJ!ct(dsgXEHD_PzHST=uw@?WK1E17DAj;
zN*w|EwAL?6DU&y#_Sb>WJoXpKFL!MA(BVttUA6Kx!+>2od~AOws2nU#N~T`3Nv9y~
zm^h|M`!<7cBlMoN0Zv;$_WFsm)?B!7f!^L;`uh5GZ*Q+kCX;57$_9&od$ratYprK~
z&O-86fXE$>&V1^7U-z<O?h}~N-3ae2NQt&J+6HZFR37F>HPRK--Qpu^f!9}X+|e<~
z6MvoI@K5qN;7er_x_I#-M~@z*udk0IM~=vunHdwn<M9kRj`IVc59m`$P5uw=(?0D=
zCiecp_+Q@oj*NHT$86R&&JpmWK}#G*<JyqSXlCaWqf?UcDaGJbR}EZojghHx1Un6U
z95^k2PZW2jbR-;gUH2uRw+Q;$uiz$g3-HO}UE4;$cU}JskOD@5X<!(b0^TTszgl*D
zzzcl1NbX00pT5l(-zE@V7Wx+e#jo41+uPIr6KfBhqy$$z_y7O^07*qoM6N<$f=Ipg
ARR910

literal 0
HcmV?d00001

diff --git a/gui/simple-greeter/plugins/smartcard/icons/Makefile.am b/gui/simple-greeter/plugins/smartcard/icons/Makefile.am
new file mode 100644
index 0000000..c20f10d
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/icons/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = 16x16 48x48
diff --git a/gui/simple-greeter/plugins/smartcard/page.ui b/gui/simple-greeter/plugins/smartcard/page.ui
new file mode 100644
index 0000000..fe6da78
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/page.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.14"/>
+    <object class="GtkVBox" id="page">
+      <property name="visible">True</property>
+      <property name="orientation">vertical</property>
+      <child>
+        <object class="GtkHBox" id="auth-input-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-prompt-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="expand">False</property>
+              <property name="fill">False</property>
+              <property name="position">0</property>
+            </packing>
+          </child>
+          <child>
+            <object class="GtkEntry" id="auth-prompt-entry">
+              <property name="visible">True</property>
+              <property name="can_focus">True</property>
+              <property name="activates_default">True</property>
+            </object>
+            <packing>
+              <property name="position">1</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">0</property>
+        </packing>
+      </child>
+      <child>
+        <object class="GtkHBox" id="auth-message-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-message-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="position">0</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">1</property>
+        </packing>
+      </child>
+    </object>
+</interface>
diff --git a/gui/simple-greeter/plugins/smartcard/plugin.c b/gui/simple-greeter/plugins/smartcard/plugin.c
new file mode 100644
index 0000000..fffbd50
--- /dev/null
+++ b/gui/simple-greeter/plugins/smartcard/plugin.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include "gdm-smartcard-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (void)
+{
+        static GObject *extension;
+
+        if (extension != NULL) {
+                g_object_ref (extension);
+        } else {
+                extension = g_object_new (GDM_TYPE_SMARTCARD_EXTENSION, NULL);
+                g_object_add_weak_pointer (extension, (gpointer *) &extension);
+        }
+
+        return GDM_GREETER_EXTENSION (extension);
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7c344c9..3432b98 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -85,6 +85,7 @@ gui/simple-greeter/gdm-user.c
 gui/simple-greeter/gdm-user-chooser-widget.c
 gui/simple-greeter/greeter-main.c
 gui/simple-greeter/plugins/password/gdm-password-extension.c
+gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
 gui/user-switch-applet/applet.c
 gui/user-switch-applet/gdm-entry-menu-item.c
 gui/user-switch-applet/GNOME_FastUserSwitchApplet.server.in.in
-- 
1.7.0.1


From 9d94f73380af5946f282fceab33f5f51db4193b4 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 23 Feb 2009 17:57:06 -0500
Subject: [PATCH 18/47] Add a new "choosable" property to show tasks in user list

Useful for Smartcard and some future "Guest" account plugin
---
 gui/simple-greeter/gdm-greeter-login-window.c      |   13 ++++++++++---
 gui/simple-greeter/libgdmsimplegreeter/gdm-task.c  |    6 ++++++
 gui/simple-greeter/libgdmsimplegreeter/gdm-task.h  |    2 ++
 .../fingerprint/gdm-fingerprint-extension.c        |    7 +++++++
 .../plugins/password/gdm-password-extension.c      |    7 +++++++
 .../plugins/smartcard/gdm-smartcard-extension.c    |    7 +++++++
 6 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index c2463d9..2c47beb 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1974,9 +1974,6 @@ gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
         g_debug ("GdmGreeterLoginWindow: new extension '%s - %s' added",
                 name, description);
 
-        g_free (name);
-        g_free (description);
-
         if (gdm_task_list_get_number_of_tasks (GDM_TASK_LIST (login_window->priv->conversation_list)) == 0) {
                 gtk_widget_hide (login_window->priv->conversation_list);
         } else {
@@ -1987,6 +1984,16 @@ gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
                                 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);
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
index f72fa78..05fd75c 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
@@ -88,6 +88,12 @@ 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)
 {
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
index 9894e65..c75bf29 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
@@ -44,6 +44,7 @@ struct _GdmTaskIface
         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);
@@ -57,6 +58,7 @@ 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 --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
index b20dd65..7aa99e7 100644
--- a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
@@ -188,12 +188,19 @@ 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
diff --git a/gui/simple-greeter/plugins/password/gdm-password-extension.c b/gui/simple-greeter/plugins/password/gdm-password-extension.c
index 3acab3f..4922c65 100644
--- a/gui/simple-greeter/plugins/password/gdm-password-extension.c
+++ b/gui/simple-greeter/plugins/password/gdm-password-extension.c
@@ -188,12 +188,19 @@ 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
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
index 6fa01fb..25d5de4 100644
--- a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
@@ -290,12 +290,19 @@ 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
-- 
1.7.0.1


From 8457e0ad2c181d6f4d67fa7525c02b3f083121df Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 24 Feb 2009 15:12:35 -0500
Subject: [PATCH 19/47] Separate handling of non-users in user list from users

Now get_chosen_user returns NULL if the activated item
wasn't a user.  We also separate the handling of on item
activation in two functions depending on the item type.

This will be useful for adding custom handling for plugin
added items.
---
 gui/simple-greeter/gdm-greeter-login-window.c      |   53 +++++++++++++-------
 gui/simple-greeter/gdm-user-chooser-widget.c       |   23 ++++++++-
 .../fingerprint/gdm-fingerprint-extension.c        |    2 -
 3 files changed, 56 insertions(+), 22 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 2c47beb..bbb99db 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1123,29 +1123,49 @@ begin_task_verification_for_selected_user (GdmTaskList           *task_list,
 }
 
 static void
-on_user_chosen (GdmUserChooserWidget  *user_chooser,
-                GdmGreeterLoginWindow *login_window)
+on_user_chosen (GdmGreeterLoginWindow *login_window,
+                const char            *user_name)
 {
-        char *user_name;
+        g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
+
+        g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+                       0, user_name);
+
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    begin_task_verification_for_selected_user,
+                                    login_window);
+
+        switch_mode (login_window, MODE_AUTHENTICATION);
+}
+
+static void
+on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
+                           GdmGreeterLoginWindow *login_window)
+{
+        char *item_id;
 
         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) {
+        if (user_name != NULL) {
+                g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
+                on_user_chosen (login_window, user_name);
+                g_free (user_name);
                 return;
         }
 
-        g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
-                       0, user_name);
+        item_id = gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (user_chooser));
+        g_debug ("GdmGreeterLoginWindow: item chosen '%s'", item_id);
 
-        if (strcmp (user_name, GDM_USER_CHOOSER_USER_OTHER) == 0) {
+        if (strcmp (item_id, GDM_USER_CHOOSER_USER_OTHER) == 0) {
+                g_debug ("GdmGreeterLoginWindow: Starting all auth conversations");
                 gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
                                             (GdmTaskListForeachFunc)
                                             begin_task_verification,
                                             login_window);
-        } 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_free (item_id);
+        } else if (strcmp (item_id, GDM_USER_CHOOSER_USER_AUTO) == 0) {
+                g_debug ("GdmGreeterLoginWindow: Starting auto login");
                 g_signal_emit (login_window, signals[BEGIN_AUTO_LOGIN], 0,
                                login_window->priv->timed_login_username);
 
@@ -1155,16 +1175,11 @@ on_user_chosen (GdmUserChooserWidget  *user_chooser,
                 /* 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 {
-                gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                            (GdmTaskListForeachFunc)
-                                            begin_task_verification_for_selected_user,
-                                            login_window);
+                g_free (item_id);
         }
 
+        g_debug ("GdmGreeterLoginWindow: Switching to shrunken authentication mode");
         switch_mode (login_window, MODE_AUTHENTICATION);
-
-        g_free (user_name);
 }
 
 static void
@@ -1443,7 +1458,7 @@ load_theme (GdmGreeterLoginWindow *login_window)
                           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",
diff --git a/gui/simple-greeter/gdm-user-chooser-widget.c b/gui/simple-greeter/gdm-user-chooser-widget.c
index 77e06a7..bff71e5 100644
--- a/gui/simple-greeter/gdm-user-chooser-widget.c
+++ b/gui/simple-greeter/gdm-user-chooser-widget.c
@@ -269,9 +269,30 @@ gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
 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 --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
index 7aa99e7..316ef46 100644
--- a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
@@ -287,8 +287,6 @@ create_page (GdmFingerprintExtension *extension)
 static void
 create_actions (GdmFingerprintExtension *extension)
 {
-        GtkAction *action;
-
         extension->priv->actions = gtk_action_group_new ("gdm-fingerprint-extension");
 }
 
-- 
1.7.0.1


From 04628a2751e7f0543ed7f5009b566dfe13662a85 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 27 Feb 2009 15:44:13 -0500
Subject: [PATCH 20/47] Initiate smart card auth when clicking on it in list

---
 gui/simple-greeter/gdm-greeter-login-window.c      |   24 ++++++++++++++++++++
 .../plugins/smartcard/gdm-smartcard-extension.c    |    2 +-
 2 files changed, 25 insertions(+), 1 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index bbb99db..342133d 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -650,6 +650,7 @@ reset_dialog (GdmGreeterLoginWindow *login_window)
 
         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));
@@ -1143,6 +1144,7 @@ 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));
@@ -1176,6 +1178,28 @@ on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
                 set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
                 set_message (login_window, _("Select language and click Log In"));
                 g_free (item_id);
+        } else {
+                GdmTask *task;
+
+                task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                                   (GdmTaskListForeachFunc)
+                                                   task_has_service_name,
+                                                   (gpointer) item_id);
+
+                if (task == NULL) {
+                        g_debug ("GdmGreeterLoginWindow: %s has no task associated with it", item_id);
+                        g_free (item_id);
+                        return;
+                }
+                g_debug ("GdmGreeterLoginWindow: Beginning auth conversation for item %s", item_id);
+
+                /* FIXME: we should probably give the plugin more say for
+                 * what happens here.
+                 */
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, item_id);
+                g_free (item_id);
+
+                gtk_widget_set_sensitive (login_window->priv->conversation_list, FALSE);
         }
 
         g_debug ("GdmGreeterLoginWindow: Switching to shrunken authentication mode");
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
index 25d5de4..a9e41f4 100644
--- a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
@@ -103,7 +103,7 @@ on_smartcard_event (GIOChannel   *io_channel,
 
                 if (extension->priv->number_of_tokens == 1) {
                         gdm_conversation_choose_user (GDM_CONVERSATION (extension),
-                                                      GDM_CONVERSATION_OTHER_USER);
+                                                      PAMSERVICENAME);
                 } else if (extension->priv->number_of_tokens == 0) {
                         gdm_conversation_cancel (GDM_CONVERSATION (extension));
                 }
-- 
1.7.0.1


From 4e0f55d8c7633e30808209172b986d2f41052de1 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 2 Mar 2009 11:10:28 -0500
Subject: [PATCH 21/47] Only show task list if user is selected

---
 gui/simple-greeter/gdm-greeter-login-window.c |   32 ++++++++++++++----------
 gui/simple-greeter/gdm-task-list.c            |   11 +++++++-
 2 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 342133d..a7571cc 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -524,6 +524,7 @@ switch_mode (GdmGreeterLoginWindow *login_window,
 {
         const char *default_name;
         GtkWidget  *box;
+        int         number_of_tasks;
 
         /* we want to run this even if we're supposed to
            be in the mode already so that we reset everything
@@ -538,15 +539,23 @@ switch_mode (GdmGreeterLoginWindow *login_window,
 
                 show_widget (login_window, "cancel-button", FALSE);
                 show_widget (login_window, "auth-page-box", FALSE);
+                show_widget (login_window, "conversation-list", FALSE);
 
                 sensitize_widget (login_window, "disconnect-button", FALSE);
 
                 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, "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:
@@ -1161,11 +1170,14 @@ on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
 
         if (strcmp (item_id, GDM_USER_CHOOSER_USER_OTHER) == 0) {
                 g_debug ("GdmGreeterLoginWindow: Starting all auth conversations");
+                g_free (item_id);
+
                 gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
                                             (GdmTaskListForeachFunc)
                                             begin_task_verification,
                                             login_window);
-                g_free (item_id);
+
+                switch_mode (login_window, MODE_AUTHENTICATION);
         } else if (strcmp (item_id, GDM_USER_CHOOSER_USER_AUTO) == 0) {
                 g_debug ("GdmGreeterLoginWindow: Starting auto login");
                 g_signal_emit (login_window, signals[BEGIN_AUTO_LOGIN], 0,
@@ -1178,6 +1190,8 @@ on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
                 set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
                 set_message (login_window, _("Select language and click Log In"));
                 g_free (item_id);
+
+                switch_mode (login_window, MODE_AUTHENTICATION);
         } else {
                 GdmTask *task;
 
@@ -1192,18 +1206,17 @@ on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
                         return;
                 }
                 g_debug ("GdmGreeterLoginWindow: Beginning auth conversation for item %s", item_id);
-
                 /* FIXME: we should probably give the plugin more say for
                  * what happens here.
                  */
                 g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, item_id);
                 g_free (item_id);
 
-                gtk_widget_set_sensitive (login_window->priv->conversation_list, FALSE);
-        }
+                switch_mode (login_window, MODE_AUTHENTICATION);
+                gdm_task_list_set_active_task (GDM_TASK_LIST (login_window->priv->conversation_list), task);
 
-        g_debug ("GdmGreeterLoginWindow: Switching to shrunken authentication mode");
-        switch_mode (login_window, MODE_AUTHENTICATION);
+                g_object_unref (task);
+        }
 }
 
 static void
@@ -1509,7 +1522,6 @@ load_theme (GdmGreeterLoginWindow *login_window)
                                   "deactivated",
                                   G_CALLBACK (on_task_deactivated),
                                   login_window);
-        gtk_widget_show (login_window->priv->conversation_list);
 
         login_window->priv->auth_banner_label = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-banner-label"));
         /*make_label_small_italic (login_window->priv->auth_banner_label);*/
@@ -2013,12 +2025,6 @@ gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
         g_debug ("GdmGreeterLoginWindow: new extension '%s - %s' added",
                 name, description);
 
-        if (gdm_task_list_get_number_of_tasks (GDM_TASK_LIST (login_window->priv->conversation_list)) == 0) {
-                gtk_widget_hide (login_window->priv->conversation_list);
-        } else {
-                gtk_widget_show (login_window->priv->conversation_list);
-        }
-
         gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
                                 GDM_TASK (extension));
 
diff --git a/gui/simple-greeter/gdm-task-list.c b/gui/simple-greeter/gdm-task-list.c
index 3dff10e..be50832 100644
--- a/gui/simple-greeter/gdm-task-list.c
+++ b/gui/simple-greeter/gdm-task-list.c
@@ -303,17 +303,24 @@ 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)) {
-                        return TRUE;
+                        was_activated = TRUE;
                 }
         }
 
-        return FALSE;
+        gtk_widget_set_sensitive (GTK_WIDGET (widget), was_sensitive);
+        return was_activated;
 }
 
 int
-- 
1.7.0.1


From 3887d52e987f04408c209a79883500ffd669b576 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 2 Mar 2009 13:53:34 -0500
Subject: [PATCH 22/47] Pull verification functions out into their own subroutines

This makes the function smaller and easier to read
---
 gui/simple-greeter/gdm-greeter-login-window.c |  135 +++++++++++++++++-------
 1 files changed, 95 insertions(+), 40 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index a7571cc..0b4db33 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1108,6 +1108,17 @@ begin_task_verification (GdmTaskList           *task_list,
         return FALSE;
 }
 
+static void
+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,
@@ -1133,6 +1144,15 @@ begin_task_verification_for_selected_user (GdmTaskList           *task_list,
 }
 
 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)
 {
@@ -1141,12 +1161,78 @@ on_user_chosen (GdmGreeterLoginWindow *login_window,
         g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
                        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)
-                                    begin_task_verification_for_selected_user,
-                                    login_window);
+                                    reset_task_if_not_given,
+                                    task);
+}
+
+static void
+begin_single_service_verification (GdmGreeterLoginWindow *login_window,
+                                   const char            *service_name)
+{
+        GdmTask *task;
+
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           (GdmTaskListForeachFunc)
+                                           task_has_service_name,
+                                           (gpointer) 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);
+
+        reset_every_task_but_given_task (login_window, task);
+        g_object_unref (task);
 }
 
 static void
@@ -1168,54 +1254,23 @@ on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
         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);
 
-                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);
+                begin_verification (login_window);
         } else if (strcmp (item_id, GDM_USER_CHOOSER_USER_AUTO) == 0) {
                 g_debug ("GdmGreeterLoginWindow: Starting auto login");
-                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"));
                 g_free (item_id);
 
-                switch_mode (login_window, MODE_AUTHENTICATION);
+                begin_auto_login (login_window);
         } else {
-                GdmTask *task;
-
-                task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                                   (GdmTaskListForeachFunc)
-                                                   task_has_service_name,
-                                                   (gpointer) item_id);
-
-                if (task == NULL) {
-                        g_debug ("GdmGreeterLoginWindow: %s has no task associated with it", item_id);
-                        g_free (item_id);
-                        return;
-                }
-                g_debug ("GdmGreeterLoginWindow: Beginning auth conversation for item %s", item_id);
-                /* FIXME: we should probably give the plugin more say for
-                 * what happens here.
-                 */
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, item_id);
+                g_debug ("GdmGreeterLoginWindow: Starting single auth conversation");
+                begin_single_service_verification (login_window, item_id);
                 g_free (item_id);
-
-                switch_mode (login_window, MODE_AUTHENTICATION);
-                gdm_task_list_set_active_task (GDM_TASK_LIST (login_window->priv->conversation_list), task);
-
-                g_object_unref (task);
         }
 }
 
-- 
1.7.0.1


From 6fa6b2e08b2358de4a7c5efe10d535c46ca3c6d0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 2 Mar 2009 13:57:34 -0500
Subject: [PATCH 23/47] Add new function find_task_with_service_name

It hides a bunch of icky foreach calls.
---
 gui/simple-greeter/gdm-greeter-login-window.c |   50 +++++++++++--------------
 1 files changed, 22 insertions(+), 28 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 0b4db33..c423e43 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -609,6 +609,20 @@ task_has_service_name (GdmTaskList *task_list,
         return has_service_name;
 }
 
+GdmTask *
+find_task_with_service_name (GdmGreeterLoginWindow *login_window,
+                             const char            *service_name)
+{
+        GdmTask *task;
+
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           (GdmTaskListForeachFunc)
+                                           task_has_service_name,
+                                           (gpointer) service_name);
+
+        return task;
+}
+
 static gboolean
 reset_task (GdmTaskList           *task_list,
             GdmTask               *task,
@@ -715,10 +729,7 @@ gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window,
 
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                           (GdmTaskListForeachFunc)
-                                           task_has_service_name,
-                                           (gpointer) service_name);
+        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))) {
@@ -747,10 +758,7 @@ gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_wind
 
         g_debug ("GdmGreeterLoginWindow: conversation '%s' has stopped", service_name);
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                           (GdmTaskListForeachFunc)
-                                           task_has_service_name,
-                                           (gpointer) service_name);
+        task = find_task_with_service_name (login_window, service_name);
 
         if (task != NULL) {
                 gdm_conversation_reset (GDM_CONVERSATION (task));
@@ -818,10 +826,7 @@ gdm_greeter_login_window_info (GdmGreeterLoginWindow *login_window,
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
         g_debug ("GdmGreeterLoginWindow: info: %s", text);
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                        (GdmTaskListForeachFunc)
-                                        task_has_service_name,
-                                        (gpointer) service_name);
+        task = find_task_with_service_name (login_window, service_name);
 
         if (task != NULL) {
                 gdm_conversation_set_message (GDM_CONVERSATION (task),
@@ -842,10 +847,7 @@ gdm_greeter_login_window_problem (GdmGreeterLoginWindow *login_window,
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
         g_debug ("GdmGreeterLoginWindow: problem: %s", text);
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                        (GdmTaskListForeachFunc)
-                                        task_has_service_name,
-                                        (gpointer) service_name);
+        task = find_task_with_service_name (login_window, service_name);
 
         if (task != NULL) {
                 gdm_conversation_set_message (GDM_CONVERSATION (task),
@@ -947,10 +949,8 @@ gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
 
         g_debug ("GdmGreeterLoginWindow: info query: %s", text);
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                        (GdmTaskListForeachFunc)
-                                        task_has_service_name,
-                                        (gpointer) service_name);
+
+        task = find_task_with_service_name (login_window, service_name);
 
         if (task != NULL) {
                 gdm_conversation_ask_question (GDM_CONVERSATION (task),
@@ -978,10 +978,7 @@ gdm_greeter_login_window_secret_info_query (GdmGreeterLoginWindow *login_window,
 
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                        (GdmTaskListForeachFunc)
-                                        task_has_service_name,
-                                        (gpointer) service_name);
+        task = find_task_with_service_name (login_window, service_name);
 
         if (task != NULL) {
                 gdm_conversation_ask_secret (GDM_CONVERSATION (task),
@@ -1211,10 +1208,7 @@ begin_single_service_verification (GdmGreeterLoginWindow *login_window,
 {
         GdmTask *task;
 
-        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
-                                           (GdmTaskListForeachFunc)
-                                           task_has_service_name,
-                                           (gpointer) service_name);
+        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);
-- 
1.7.0.1


From 2360e9f39f1dcb54ca8928eb7eaec0d64df565e0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 2 Mar 2009 17:09:16 -0500
Subject: [PATCH 24/47] Notify plugins if their user choose requests fail

This allows the smart card plugin to cancel pending
conversations when a card gets inserted.

This isn't perfect.  We really want to only cancel
the conversations if they're for a user other
than the user the smartcard is for.
---
 common/gdm-marshal.list                            |    1 +
 gui/simple-greeter/gdm-greeter-login-window.c      |   12 ++++++++-
 gui/simple-greeter/libgdmsimplegreeter/Makefile.am |    2 +
 .../libgdmsimplegreeter/gdm-conversation.c         |   19 +++++++++++-----
 .../libgdmsimplegreeter/gdm-conversation.h         |    6 ++--
 .../plugins/smartcard/gdm-smartcard-extension.c    |   23 +++++++++++++++----
 6 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/common/gdm-marshal.list b/common/gdm-marshal.list
index d5455e1..d8a9e72 100644
--- a/common/gdm-marshal.list
+++ b/common/gdm-marshal.list
@@ -5,3 +5,4 @@ VOID:STRING,STRING
 VOID:UINT,UINT
 VOID:STRING,INT
 VOID:DOUBLE
+BOOLEAN:STRING
diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index c423e43..b2af64e 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1901,7 +1901,7 @@ on_conversation_cancel (GdmGreeterLoginWindow *login_window,
         do_cancel (login_window);
 }
 
-static void
+static gboolean
 on_conversation_chose_user (GdmGreeterLoginWindow *login_window,
                             const char            *username,
                             GdmConversation       *conversation)
@@ -1912,7 +1912,13 @@ on_conversation_chose_user (GdmGreeterLoginWindow *login_window,
                 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;
+                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),
@@ -1920,6 +1926,8 @@ on_conversation_chose_user (GdmGreeterLoginWindow *login_window,
                 gdm_user_chooser_widget_set_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser),
                                                               username);
         }
+
+        return TRUE;
 }
 
 void
diff --git a/gui/simple-greeter/libgdmsimplegreeter/Makefile.am b/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
index 1ef5725..0d7a0bd 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
+++ b/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
@@ -3,6 +3,7 @@ NULL =
 AM_CPPFLAGS = \
 	-I.					\
 	-I..					\
+	-I$(top_srcdir)/common			\
 	-DBINDIR=\"$(bindir)\"			\
 	-DDATADIR=\"$(datadir)\"		\
 	-DLIBDIR=\"$(libdir)\"			\
@@ -28,6 +29,7 @@ libgdmsimplegreeter_la_SOURCES =		\
 
 libgdmsimplegreeter_la_LIBADD =			\
 	$(GTK_LIBS)				\
+	$(top_builddir)/common/libgdmcommon.la	\
 	$(NULL)
 
 libgdmsimplegreeter_la_LDFLAGS = 		\
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
index cef435c..ee763ef 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
@@ -25,9 +25,9 @@
 #include <gtk/gtk.h>
 
 #include "gdm-conversation.h"
+#include "gdm-marshal.h"
 #include "gdm-task.h"
 
-
 enum {
         ANSWER,
         USER_CHOSEN,
@@ -76,12 +76,12 @@ gdm_conversation_class_init (gpointer g_iface)
         signals [USER_CHOSEN] =
                 g_signal_new ("user-chosen",
                               iface_type,
-                              G_SIGNAL_RUN_FIRST,
+                              G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GdmConversationIface, user_chosen),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
-                              G_TYPE_NONE,
+                              gdm_marshal_BOOLEAN__STRING,
+                              G_TYPE_BOOLEAN,
                               1, G_TYPE_STRING);
         signals [CANCEL] =
                 g_signal_new ("cancel",
@@ -171,9 +171,16 @@ gdm_conversation_cancel (GdmConversation   *conversation)
 {
         g_signal_emit (conversation, signals [CANCEL], 0);
 }
-void
+
+gboolean
 gdm_conversation_choose_user (GdmConversation *conversation,
                               const char      *username)
 {
-        g_signal_emit (conversation, signals [USER_CHOSEN], 0, username);
+        gboolean was_chosen;
+
+        was_chosen = FALSE;
+
+        g_signal_emit (conversation, signals [USER_CHOSEN], 0, username, &was_chosen);
+
+        return was_chosen;
 }
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
index fb4bf49..b37b21e 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
@@ -61,7 +61,7 @@ struct _GdmConversationIface
         /* signals */
         char * (* answer)       (GdmConversation *conversation);
         void   (* cancel)       (GdmConversation *conversation);
-        void   (* user_chosen)     (GdmConversation *conversation);
+        gboolean  (* user_chosen)  (GdmConversation *conversation);
 };
 
 GType  gdm_conversation_get_type     (void) G_GNUC_CONST;
@@ -85,8 +85,8 @@ gboolean   gdm_conversation_focus    (GdmConversation *conversation);
 void   gdm_conversation_answer (GdmConversation   *conversation,
                                 const char        *answer);
 void   gdm_conversation_cancel (GdmConversation   *conversation);
-void   gdm_conversation_choose_user (GdmConversation   *conversation,
-                                     const char        *username);
+gboolean  gdm_conversation_choose_user (GdmConversation   *conversation,
+                                        const char        *username);
 
 G_END_DECLS
 
diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
index a9e41f4..274132e 100644
--- a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
@@ -53,6 +53,7 @@ struct _GdmSmartcardExtensionPrivate
         int        number_of_tokens;
 
         guint      answer_pending : 1;
+        guint      select_when_ready : 1;
 };
 
 static void gdm_smartcard_extension_finalize (GObject *object);
@@ -102,8 +103,14 @@ on_smartcard_event (GIOChannel   *io_channel,
                 }
 
                 if (extension->priv->number_of_tokens == 1) {
-                        gdm_conversation_choose_user (GDM_CONVERSATION (extension),
-                                                      PAMSERVICENAME);
+                        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));
                 }
@@ -210,10 +217,16 @@ 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)
-          {
+        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 *
-- 
1.7.0.1


From e1af827b80baba71e6b5f9b36122d75aa41ee52c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 13 Apr 2009 14:19:50 -0400
Subject: [PATCH 25/47] reset all conversations if password conversation fails

This is a temporary hack until we store plugin policy in
gconf.
---
 gui/simple-greeter/gdm-greeter-login-window.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index b2af64e..6ce65a0 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -758,6 +758,16 @@ gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_wind
 
         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) {
-- 
1.7.0.1


From 97fdd612730c2aa42919d01ad1e965e261690f1e Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Apr 2009 10:25:18 -0400
Subject: [PATCH 26/47] When one PAM conversation wins, stop the others

This doesn't work yet, it's still in progress code.
---
 daemon/gdm-session-direct.c |   64 ++++++++++++++++++++++++++++++-------------
 daemon/gdm-simple-slave.c   |    2 +-
 2 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 09d6a76..86ceff8 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -2300,6 +2300,40 @@ gdm_session_direct_open_session (GdmSession *session,
 }
 
 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) {
+                        continue;
+                }
+
+                stop_conversation (conversation);
+        }
+
+        g_hash_table_remove_all (session->priv->conversations);
+}
+
+static void
 gdm_session_direct_start_session (GdmSession *session,
                                   const char *service_name)
 {
@@ -2311,6 +2345,16 @@ gdm_session_direct_start_session (GdmSession *session,
         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);
 
         if (gdm_session_direct_bypasses_xsession (impl)) {
@@ -2321,8 +2365,6 @@ gdm_session_direct_start_session (GdmSession *session,
 
         g_free (command);
 
-        conversation = find_conversation_by_name (impl, service_name);
-
         setup_session_environment (impl);
         send_environment (impl, conversation);
 
@@ -2333,23 +2375,7 @@ gdm_session_direct_start_session (GdmSession *session,
 static void
 stop_all_conversations (GdmSessionDirect *session)
 {
-        GHashTableIter iter;
-        gpointer key, value;
-
-        if (session->priv->conversations == NULL) {
-                return;
-        }
-
-        g_hash_table_iter_init (&iter, session->priv->conversations);
-        while (g_hash_table_iter_next (&iter, &key, &value)) {
-                GdmSessionConversation *conversation;
-
-                conversation = (GdmSessionConversation *) value;
-
-                stop_conversation (conversation);
-        }
-
-        g_hash_table_remove_all (session->priv->conversations);
+        stop_all_other_conversations (session, NULL);
 }
 
 static void
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 86aa706..efe7fc7 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -586,7 +586,7 @@ on_session_conversation_stopped (GdmSession     *session,
         gboolean res;
         g_debug ("GdmSimpleSlave: conversation stopped");
 
-        if (slave->priv->greeter_server != NULL) {
+        if (slave->priv->greeter != NULL) {
                 res = gdm_greeter_server_conversation_stopped (slave->priv->greeter_server,
                                                                service_name);
                 if (! res) {
-- 
1.7.0.1


From bdcc36eb922ce411bc3abcd6eca31e449fc5666c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Apr 2009 15:30:28 -0400
Subject: [PATCH 27/47] When one PAM conv. wins, actually stop the others

We weren't properly keeping the winning conversation
around in the previous commit
---
 daemon/gdm-session-direct.c |   13 ++++++++++---
 1 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 86ceff8..158a5f1 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -2324,13 +2324,20 @@ stop_all_other_conversations (GdmSessionDirect        *session,
                 conversation = (GdmSessionConversation *) value;
 
                 if (conversation == conversation_to_keep) {
-                        continue;
+                        g_hash_table_iter_steal (&iter);
+                        g_free (key);
+                } else {
+                        stop_conversation (conversation);
                 }
-
-                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
-- 
1.7.0.1


From 4b901ff9f4d48244cc48df1837eafd184143d096 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 24 Jul 2009 14:41:48 -0400
Subject: [PATCH 28/47] KILL pam stack instead of TERM pam stack

Some PAM modules are really slow to shut down.
We need to handle them being slow to shut down better,
(by not blocking login on them shutting down etc), but
in the mean time force them to die immediately.

This is a temporary hack.
---
 daemon/gdm-session-worker-job.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c
index 0327d77..d99b8a5 100644
--- a/daemon/gdm-session-worker-job.c
+++ b/daemon/gdm-session-worker-job.c
@@ -320,7 +320,7 @@ gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job)
 
         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");
-- 
1.7.0.1


From 6c35264f2715099bad657183d82899ab56bf4b8e Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 21 Oct 2009 16:08:52 -0400
Subject: [PATCH 29/47] Don't show tasklist for autologin

---
 gui/simple-greeter/gdm-greeter-login-window.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 6ce65a0..d0d7363 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1187,6 +1187,11 @@ begin_auto_login (GdmGreeterLoginWindow *login_window)
         set_message (login_window, _("Select language and click Log In"));
 
         switch_mode (login_window, MODE_AUTHENTICATION);
+
+        show_widget (login_window, "conversation-list", FALSE);
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc) reset_task,
+                                    login_window);
 }
 
 static gboolean
-- 
1.7.0.1


From 9fe9625d2ca6d8378f00bc520071b0b339ad41c6 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 23 Oct 2009 17:39:19 -0400
Subject: [PATCH 30/47] Drop the other hiding stuff for now.

It depends on buttons being available that we don't have
in the multi-stack branch.
---
 gui/simple-greeter/gdm-user-chooser-widget.c |    8 +-------
 1 files changed, 1 insertions(+), 7 deletions(-)

diff --git a/gui/simple-greeter/gdm-user-chooser-widget.c b/gui/simple-greeter/gdm-user-chooser-widget.c
index bff71e5..4d3c445 100644
--- a/gui/simple-greeter/gdm-user-chooser-widget.c
+++ b/gui/simple-greeter/gdm-user-chooser-widget.c
@@ -140,13 +140,7 @@ update_other_user_visibility (GdmUserChooserWidget *widget)
                 return;
         }
 
-        number_of_users = gdm_chooser_widget_get_number_of_items (GDM_CHOOSER_WIDGET (widget));
-
-        /* we hide the Other user if it's the last one, and we show it
-         * if there's another user */
-        if (number_of_users == 1 && widget->priv->has_user_other) {
-                remove_user_other (widget);
-        } if (number_of_users >= 1 && !widget->priv->has_user_other) {
+        if (!widget->priv->has_user_other) {
                 add_user_other (widget);
         }
 }
-- 
1.7.0.1


From 77282fbe8b3f7cee8bf498714a3f5145af047ce8 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 11:13:10 -0400
Subject: [PATCH 31/47] Prevent start session signal handler from getting called multiple times

It was causing a double free.
---
 gui/simple-greeter/gdm-greeter-login-window.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index d0d7363..5bd3019 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -906,8 +906,17 @@ on_ready_to_start_session (GdmGreeterLoginWindow *login_window,
                            GParamSpec            *param_spec,
                            char                  *service_name)
 {
+        if (!login_window->priv->is_interactive) {
+                return;
+        }
+
         gdm_greeter_login_window_start_session_when_ready (login_window, service_name);
         g_free (service_name);
+
+        if (login_window->priv->start_session_handler_id > 0) {
+                g_signal_handler_disconnect (login_window, login_window->priv->start_session_handler_id);
+                login_window->priv->start_session_handler_id = 0;
+        }
 }
 
 static void
-- 
1.7.0.1


From 64546b1a830c050eb79cf4b47ad24a652d4f1cc9 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 13:57:53 -0400
Subject: [PATCH 32/47] Don't show unimportant images on plugin buttons

---
 gui/simple-greeter/gdm-greeter-login-window.c |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 5bd3019..e185efb 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1988,8 +1988,14 @@ on_button_action_icon_name_changed (GtkWidget *button)
 
         action = gtk_widget_get_action (button);
 
-        image = gtk_action_create_icon (GTK_ACTION (action), GTK_ICON_SIZE_BUTTON);
+        if (gtk_action_get_is_important (action)) {
+                image = gtk_action_create_icon (GTK_ACTION (action), GTK_ICON_SIZE_BUTTON);
+        } else {
+                image = NULL;
+        }
+
         gtk_button_set_image (GTK_BUTTON (button), image);
+
 }
 
 static void
-- 
1.7.0.1


From b8be6cb582874c1557adc48cef67557e5ae4e411 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 14:10:35 -0400
Subject: [PATCH 33/47] Add visibility concept to task iface

---
 gui/simple-greeter/libgdmsimplegreeter/gdm-task.c |    6 ++++++
 gui/simple-greeter/libgdmsimplegreeter/gdm-task.h |    2 ++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
index 05fd75c..858b1ef 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
@@ -94,6 +94,12 @@ gdm_task_is_choosable (GdmTask *task)
         return GDM_TASK_GET_IFACE (task)->is_choosable (task);
 }
 
+gboolean
+gdm_task_is_visible (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->is_visible (task);
+}
+
 static void
 gdm_task_class_init (gpointer g_iface)
 {
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
index c75bf29..51e2b0a 100644
--- a/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
@@ -45,6 +45,7 @@ struct _GdmTaskIface
         char *  (* get_description) (GdmTask   *task);
         char *  (* get_name)        (GdmTask   *task);
         gboolean  (* is_choosable)    (GdmTask   *task);
+        gboolean  (* is_visible)    (GdmTask   *task);
         /* signals */
         void (* enabled) (GdmTask *task);
         void (* disabled) (GdmTask *task);
@@ -59,6 +60,7 @@ void   gdm_task_set_enabled     (GdmTask   *task,
                                  gboolean   should_enable);
 gboolean   gdm_task_is_enabled     (GdmTask   *task);
 gboolean   gdm_task_is_choosable   (GdmTask   *task);
+gboolean   gdm_task_is_visible   (GdmTask   *task);
 G_END_DECLS
 
 #endif /* __GDM_TASK_H */
-- 
1.7.0.1


From bc43b3303ca3c6c3080784c222ebe3e460eb14c6 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 14:10:50 -0400
Subject: [PATCH 34/47] adapt fingerprint to task iface

---
 .../fingerprint/gdm-fingerprint-extension.c        |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
index 316ef46..e1fc0ed 100644
--- a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
@@ -194,6 +194,12 @@ gdm_fingerprint_extension_is_choosable (GdmTask *task)
         return FALSE;
 }
 
+gboolean
+gdm_fingerprint_extension_is_visible (GdmTask *task)
+{
+        return TRUE;
+}
+
 static void
 gdm_task_iface_init (GdmTaskIface *iface)
 {
@@ -201,6 +207,7 @@ gdm_task_iface_init (GdmTaskIface *iface)
         iface->get_description = gdm_fingerprint_extension_get_description;
         iface->get_name = gdm_fingerprint_extension_get_name;
         iface->is_choosable = gdm_fingerprint_extension_is_choosable;
+        iface->is_visible = gdm_fingerprint_extension_is_visible;
 }
 
 static void
-- 
1.7.0.1


From 36ce2ee1769d08c8260af73e26a83972204df0b7 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 14:11:05 -0400
Subject: [PATCH 35/47] adapt smartcard plugin to task iface

---
 .../plugins/smartcard/gdm-smartcard-extension.c    |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
index 274132e..9967d5f 100644
--- a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
@@ -309,6 +309,12 @@ gdm_smartcard_extension_is_choosable (GdmTask *task)
         return TRUE;
 }
 
+gboolean
+gdm_smartcard_extension_is_visible (GdmTask *task)
+{
+        return TRUE;
+}
+
 static void
 gdm_task_iface_init (GdmTaskIface *iface)
 {
@@ -316,6 +322,7 @@ gdm_task_iface_init (GdmTaskIface *iface)
         iface->get_description = gdm_smartcard_extension_get_description;
         iface->get_name = gdm_smartcard_extension_get_name;
         iface->is_choosable = gdm_smartcard_extension_is_choosable;
+        iface->is_visible = gdm_smartcard_extension_is_visible;
 }
 
 static void
-- 
1.7.0.1


From 565b1f2cee8a53e997ed7f0463c0e99e0d2f8cfc Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 14:10:50 -0400
Subject: [PATCH 36/47] adapt password to task iface

---
 .../plugins/password/gdm-password-extension.c      |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/plugins/password/gdm-password-extension.c b/gui/simple-greeter/plugins/password/gdm-password-extension.c
index 4922c65..255283e 100644
--- a/gui/simple-greeter/plugins/password/gdm-password-extension.c
+++ b/gui/simple-greeter/plugins/password/gdm-password-extension.c
@@ -194,6 +194,12 @@ gdm_password_extension_is_choosable (GdmTask *task)
         return FALSE;
 }
 
+gboolean
+gdm_password_extension_is_visible (GdmTask *task)
+{
+        return TRUE;
+}
+
 static void
 gdm_task_iface_init (GdmTaskIface *iface)
 {
@@ -201,6 +207,7 @@ gdm_task_iface_init (GdmTaskIface *iface)
         iface->get_description = gdm_password_extension_get_description;
         iface->get_name = gdm_password_extension_get_name;
         iface->is_choosable = gdm_password_extension_is_choosable;
+        iface->is_visible = gdm_password_extension_is_visible;
 }
 
 static void
-- 
1.7.0.1


From e574f31ea1ba0154564496837cba4a6f53175dfc Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 16:05:14 -0400
Subject: [PATCH 37/47] Return a different error code for "service won't work" than "auth failed"

If we bubble it up to the greeter then we should be able to have
a more sensible UI when e.g. fingerprinting isn't enabled.
---
 daemon/gdm-session-worker.c |   12 ++++++++++--
 daemon/gdm-session-worker.h |    1 +
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 8328910..adc8dcf 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -1294,7 +1294,7 @@ gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
                  */
                 g_set_error (error,
                              GDM_SESSION_WORKER_ERROR,
-                             GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+                             GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
                              _("error initiating conversation with authentication system - %s"),
                              error_code == PAM_ABORT? _("general failure") :
                              error_code == PAM_BUF_ERR? _("out of memory") :
@@ -1406,7 +1406,15 @@ gdm_session_worker_authenticate_user (GdmSessionWorker *worker,
         /* blocking call, does the actual conversation */
         error_code = pam_authenticate (worker->priv->pam_handle, authentication_flags);
 
-        if (error_code != PAM_SUCCESS) {
+        if (error_code == PAM_AUTHINFO_UNAVAIL) {
+                g_debug ("GdmSessionWorker: authentication service unavailable");
+
+                g_set_error (error,
+                             GDM_SESSION_WORKER_ERROR,
+                             GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
+                             "%s", pam_strerror (worker->priv->pam_handle, error_code));
+                goto out;
+        } else if (error_code != PAM_SUCCESS) {
                 g_debug ("GdmSessionWorker: authentication returned %d: %s", error_code, pam_strerror (worker->priv->pam_handle, error_code));
 
                 /*
diff --git a/daemon/gdm-session-worker.h b/daemon/gdm-session-worker.h
index ee5465a..b1c8285 100644
--- a/daemon/gdm-session-worker.h
+++ b/daemon/gdm-session-worker.h
@@ -41,6 +41,7 @@ typedef enum _GdmSessionWorkerError {
         GDM_SESSION_WORKER_ERROR_OPENING_MESSAGE_PIPE,
         GDM_SESSION_WORKER_ERROR_COMMUNICATING,
         GDM_SESSION_WORKER_ERROR_WORKER_DIED,
+        GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
         GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
         GDM_SESSION_WORKER_ERROR_AUTHORIZING,
         GDM_SESSION_WORKER_ERROR_OPENING_LOG_FILE,
-- 
1.7.0.1


From 564e1def472c57bd48d3f277f3adf89653665615 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 21:32:00 -0400
Subject: [PATCH 38/47] Emit "service-unavailable" from session when pam service refuses to work

---
 daemon/gdm-session-direct.c  |   26 +++++++++++++++++++++++---
 daemon/gdm-session-private.h |    2 ++
 daemon/gdm-session-relay.c   |   33 +++++++++++++++++++++++++++++++++
 daemon/gdm-session-worker.c  |   29 ++++++++++++++++++++++-------
 daemon/gdm-session.c         |   21 +++++++++++++++++++++
 daemon/gdm-session.h         |    2 ++
 6 files changed, 103 insertions(+), 10 deletions(-)

diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 158a5f1..2fca5e5 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -280,9 +280,27 @@ on_session_exited (GdmSession *session,
 }
 
 static DBusHandlerResult
-gdm_session_direct_handle_setup_complete (GdmSessionDirect *session,
-                                          GdmSessionConversation *conversation,
-                                          DBusMessage      *message)
+gdm_session_direct_handle_service_unavailable (GdmSessionDirect *session,
+                                               GdmSessionConversation *conversation,
+                                               DBusMessage      *message)
+{
+        DBusMessage *reply;
+
+        g_debug ("GdmSessionDirect: Emitting 'service-unavailable' signal");
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        _gdm_session_service_unavailable (GDM_SESSION (session), conversation->service_name);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+gdm_session_direct_handle_setup_complete  (GdmSessionDirect *session,
+                                           GdmSessionConversation *conversation,
+                                           DBusMessage      *message)
 {
         DBusMessage *reply;
 
@@ -1275,6 +1293,8 @@ session_worker_message (DBusConnection *connection,
                 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, conversation, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "ServiceUnavailable")) {
+                return gdm_session_direct_handle_service_unavailable (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SetupComplete")) {
                 return gdm_session_direct_handle_setup_complete (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SetupFailed")) {
diff --git a/daemon/gdm-session-private.h b/daemon/gdm-session-private.h
index 36781dd..e0a810e 100644
--- a/daemon/gdm-session-private.h
+++ b/daemon/gdm-session-private.h
@@ -31,6 +31,8 @@ void             _gdm_session_conversation_started         (GdmSession   *sessio
                                                             const char   *service_name);
 void             _gdm_session_conversation_stopped         (GdmSession   *session,
                                                             const char   *service_name);
+void             _gdm_session_service_unavailable          (GdmSession   *session,
+                                                            const char   *service_name);
 void             _gdm_session_setup_complete               (GdmSession   *session,
                                                             const char   *service_name);
 void             _gdm_session_setup_failed                 (GdmSession   *session,
diff --git a/daemon/gdm-session-relay.c b/daemon/gdm-session-relay.c
index 98ccc51..88aee14 100644
--- a/daemon/gdm-session-relay.c
+++ b/daemon/gdm-session-relay.c
@@ -463,6 +463,34 @@ handle_problem (GdmSessionRelay *session_relay,
 }
 
 static DBusHandlerResult
+handle_service_unavailable (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: ServiceUnavailable");
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        _gdm_session_service_unavailable (GDM_SESSION (session_relay), service_name);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
 handle_setup_complete (GdmSessionRelay *session_relay,
                        DBusConnection  *connection,
                        DBusMessage     *message)
@@ -841,6 +869,8 @@ session_handle_child_message (DBusConnection *connection,
                 return handle_info (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "Problem")) {
                 return handle_problem (session_relay, connection, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "ServiceUnavailable")) {
+                return handle_service_unavailable (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SetupComplete")) {
                 return handle_setup_complete (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SetupFailed")) {
@@ -898,6 +928,9 @@ do_introspect (DBusConnection *connection,
                                "    <method name=\"ConversationStarted\">\n"
                                "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
+                               "    <method name=\"ServiceUnavailable\">\n"
+                               "      <arg name=\"message\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
                                "    <method name=\"SetupComplete\">\n"
                                "    </method>\n"
                                "    <method name=\"SetupFailed\">\n"
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index adc8dcf..4b77249 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -2364,9 +2364,16 @@ do_setup (GdmSessionWorker *worker)
                                                  worker->priv->display_device,
                                                  &error);
         if (! res) {
-                send_dbus_string_method (worker->priv->connection,
-                                         "SetupFailed",
-                                         error->message);
+                if (g_error_matches (error,
+                                     GDM_SESSION_WORKER_ERROR,
+                                     GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE)) {
+                        send_dbus_void_method (worker->priv->connection,
+                                               "ServiceUnavailable");
+                } else {
+                        send_dbus_string_method (worker->priv->connection,
+                                                 "SetupFailed",
+                                                 error->message);
+                }
                 g_error_free (error);
                 return;
         }
@@ -2387,10 +2394,18 @@ do_authenticate (GdmSessionWorker *worker)
                                                     worker->priv->password_is_required,
                                                     &error);
         if (! res) {
-                g_debug ("GdmSessionWorker: Unable to verify user");
-                send_dbus_string_method (worker->priv->connection,
-                                         "AuthenticationFailed",
-                                         error->message);
+                if (g_error_matches (error,
+                                     GDM_SESSION_WORKER_ERROR,
+                                     GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE)) {
+                        g_debug ("GdmSessionWorker: Unable to use authentication service");
+                        send_dbus_void_method (worker->priv->connection,
+                                               "ServiceUnavailable");
+                } else {
+                        g_debug ("GdmSessionWorker: Unable to verify user");
+                        send_dbus_string_method (worker->priv->connection,
+                                                 "AuthenticationFailed",
+                                                 error->message);
+                }
                 g_error_free (error);
                 return;
         }
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 8858071..8c4548a 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -31,6 +31,7 @@
 enum {
         CONVERSATION_STARTED = 0,
         CONVERSATION_STOPPED,
+        SERVICE_UNAVAILABLE,
         SETUP_COMPLETE,
         SETUP_FAILED,
         RESET_COMPLETE,
@@ -251,6 +252,17 @@ gdm_session_class_init (gpointer g_iface)
                               g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
                               1, G_TYPE_STRING);
+        signals [SERVICE_UNAVAILABLE] =
+                g_signal_new ("service-unavailable",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionIface, service_unavailable),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_STRING);
         signals [SETUP_COMPLETE] =
                 g_signal_new ("setup-complete",
                               iface_type,
@@ -525,6 +537,15 @@ gdm_session_class_init (gpointer g_iface)
 }
 
 void
+_gdm_session_service_unavailable (GdmSession   *session,
+                                  const char   *service_name)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+
+        g_signal_emit (session, signals [SERVICE_UNAVAILABLE], 0, service_name);
+}
+
+void
 _gdm_session_setup_complete (GdmSession   *session,
                              const char   *service_name)
 {
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index 22c2ccb..9636b92 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -49,6 +49,8 @@ struct _GdmSessionIface
                                               const char   *service_name);
         void (* stop_conversation)           (GdmSession   *session,
                                               const char   *service_name);
+        void (* service_unavailable)         (GdmSession   *session,
+                                              const char   *service_name);
         void (* setup)                       (GdmSession   *session,
                                               const char   *service_name);
         void (* setup_for_user)              (GdmSession   *session,
-- 
1.7.0.1


From bf6f9b5587ae691263a4d6607ca869e016775160 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 21:38:52 -0400
Subject: [PATCH 39/47] Bubble service-unavailable up to greeter

---
 daemon/gdm-greeter-server.c |    8 ++++++++
 daemon/gdm-greeter-server.h |    2 ++
 daemon/gdm-simple-slave.c   |   17 +++++++++++++++++
 3 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index ecb2ad6..1ae64a7 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -285,6 +285,14 @@ gdm_greeter_server_problem (GdmGreeterServer *greeter_server,
 }
 
 gboolean
+gdm_greeter_server_service_unavailable (GdmGreeterServer *greeter_server,
+                                        const char       *service_name)
+{
+        send_dbus_string_signal (greeter_server, "ServiceUnavailable", service_name);
+        return TRUE;
+}
+
+gboolean
 gdm_greeter_server_reset (GdmGreeterServer *greeter_server)
 {
         send_dbus_void_signal (greeter_server, "Reset");
diff --git a/daemon/gdm-greeter-server.h b/daemon/gdm-greeter-server.h
index 976f0b7..c1da2f4 100644
--- a/daemon/gdm-greeter-server.h
+++ b/daemon/gdm-greeter-server.h
@@ -93,6 +93,8 @@ gboolean            gdm_greeter_server_info                  (GdmGreeterServer *
 gboolean            gdm_greeter_server_problem               (GdmGreeterServer *greeter_server,
                                                               const char       *service_name,
                                                               const char       *text);
+gboolean            gdm_greeter_server_service_unavailable   (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name);
 gboolean            gdm_greeter_server_reset                 (GdmGreeterServer *greeter_server);
 gboolean            gdm_greeter_server_ready                 (GdmGreeterServer *greeter_server,
                                                               const char       *service_name);
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index efe7fc7..13b47d7 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -211,6 +211,19 @@ queue_greeter_reset (GdmSimpleSlave *slave)
 }
 
 static void
+on_session_service_unavailable (GdmSession     *session,
+                                const char     *service_name,
+                                GdmSimpleSlave *slave)
+{
+        if (slave->priv->greeter_server != NULL) {
+                gdm_greeter_server_service_unavailable (slave->priv->greeter_server,
+                                                        service_name);
+        }
+
+        gdm_session_stop_conversation (session, service_name);
+}
+
+static void
 on_session_setup_complete (GdmSession     *session,
                            const char     *service_name,
                            GdmSimpleSlave *slave)
@@ -688,6 +701,10 @@ create_new_session (GdmSimpleSlave *slave)
                           G_CALLBACK (on_session_conversation_stopped),
                           slave);
         g_signal_connect (slave->priv->session,
+                          "service-unavailable",
+                          G_CALLBACK (on_session_service_unavailable),
+                          slave);
+        g_signal_connect (slave->priv->session,
                           "setup-complete",
                           G_CALLBACK (on_session_setup_complete),
                           slave);
-- 
1.7.0.1


From 0639204367cbd9094611c927f2d76292be62a0fe Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 21:46:39 -0400
Subject: [PATCH 40/47] Catch service-unavailable from server in client and propagate it

---
 gui/simple-greeter/gdm-greeter-client.c |   20 ++++++++++++++++++++
 gui/simple-greeter/gdm-greeter-client.h |    2 ++
 2 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-client.c b/gui/simple-greeter/gdm-greeter-client.c
index 12572fe..6a26f16 100644
--- a/gui/simple-greeter/gdm-greeter-client.c
+++ b/gui/simple-greeter/gdm-greeter-client.c
@@ -63,6 +63,7 @@ enum {
         PROBLEM,
         INFO_QUERY,
         SECRET_INFO_QUERY,
+        SERVICE_UNAVAILABLE,
         READY,
         CONVERSATION_STOPPED,
         RESET,
@@ -264,6 +265,13 @@ on_problem (GdmGreeterClient *client,
 }
 
 static void
+on_service_unavailable (GdmGreeterClient *client,
+                        DBusMessage      *message)
+{
+        emit_string_signal_for_message (client, "ServiceUnavailable", message, SERVICE_UNAVAILABLE);
+}
+
+static void
 on_ready (GdmGreeterClient *client,
           DBusMessage      *message)
 {
@@ -766,6 +774,8 @@ client_dbus_handle_message (DBusConnection *connection,
                 on_info (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Problem")) {
                 on_problem (client, message);
+        } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "ServiceUnavailable")) {
+                on_service_unavailable (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")) {
@@ -999,6 +1009,16 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
                               2,
                               G_TYPE_STRING, G_TYPE_STRING);
 
+        gdm_greeter_client_signals[SERVICE_UNAVAILABLE] =
+                g_signal_new ("service-unavailable",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterClientClass, service_unavailable),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE, 1, G_TYPE_STRING);
+
         gdm_greeter_client_signals[READY] =
                 g_signal_new ("ready",
                               G_OBJECT_CLASS_TYPE (object_class),
diff --git a/gui/simple-greeter/gdm-greeter-client.h b/gui/simple-greeter/gdm-greeter-client.h
index f879307..801bae4 100644
--- a/gui/simple-greeter/gdm-greeter-client.h
+++ b/gui/simple-greeter/gdm-greeter-client.h
@@ -59,6 +59,8 @@ typedef struct
         void (* problem)                 (GdmGreeterClient  *client,
                                           const char        *service_name,
                                           const char        *problem);
+        void (* service_unavailable)     (GdmGreeterClient  *client,
+                                          const char        *service_name);
         void (* ready)                   (GdmGreeterClient  *client,
                                           const char        *service_name);
         void (* conversation_stopped)    (GdmGreeterClient  *client,
-- 
1.7.0.1


From 3c01ce811187603615ca73b5c62f3f0848a589aa Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 22:09:45 -0400
Subject: [PATCH 41/47] Add gdm_task_list_remove_task

---
 gui/simple-greeter/gdm-task-list.c |   29 +++++++++++++++++++++++++++++
 gui/simple-greeter/gdm-task-list.h |    3 +++
 2 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-task-list.c b/gui/simple-greeter/gdm-task-list.c
index be50832..dd77ed6 100644
--- a/gui/simple-greeter/gdm-task-list.c
+++ b/gui/simple-greeter/gdm-task-list.c
@@ -211,6 +211,35 @@ gdm_task_list_add_task (GdmTaskList *task_list,
         }
 }
 
+void
+gdm_task_list_remove_task (GdmTaskList *task_list,
+                           GdmTask     *task)
+{
+        GtkWidget *image;
+        GtkWidget *button;
+        GIcon     *icon;
+        char      *description;
+
+        task_list->priv->tasks = g_list_remove (task_list->priv->tasks, task);
+
+        button = g_object_get_data (G_OBJECT (task), "gdm-task-list-button");
+
+        if (button != NULL) {
+            g_signal_handlers_disconnect_by_func (G_OBJECT (task),
+                                                  G_CALLBACK (on_task_enabled),
+                                                  task_list);
+            g_signal_handlers_disconnect_by_func (G_OBJECT (task),
+                                                  G_CALLBACK (on_task_disabled),
+                                                  task_list);
+            gtk_widget_destroy (button);
+            g_object_set_data (G_OBJECT (task), "gdm-task-list-button", NULL);
+        }
+
+        g_object_unref (task);
+
+        activate_first_available_task (task_list);
+}
+
 static void
 gdm_task_list_class_init (GdmTaskListClass *klass)
 {
diff --git a/gui/simple-greeter/gdm-task-list.h b/gui/simple-greeter/gdm-task-list.h
index 8bc0c0e..3df5415 100644
--- a/gui/simple-greeter/gdm-task-list.h
+++ b/gui/simple-greeter/gdm-task-list.h
@@ -74,6 +74,9 @@ GdmTask *   gdm_task_list_foreach_task (GdmTaskList           *widget,
 void        gdm_task_list_add_task        (GdmTaskList *widget,
                                            GdmTask     *task);
 
+void        gdm_task_list_remove_task        (GdmTaskList *widget,
+                                              GdmTask     *task);
+
 int         gdm_task_list_get_number_of_tasks (GdmTaskList *widget);
 G_END_DECLS
 
-- 
1.7.0.1


From 6454f9efb7aa5b9f73ac35c6622094ef0703eeb3 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 21:58:44 -0400
Subject: [PATCH 42/47] Remove task from task list if unavailable

---
 gui/simple-greeter/gdm-greeter-login-window.c |   20 ++++++++++++++++++++
 gui/simple-greeter/gdm-greeter-login-window.h |    3 +++
 gui/simple-greeter/gdm-greeter-session.c      |   14 ++++++++++++++
 3 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index e185efb..03858e3 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -870,6 +870,26 @@ gdm_greeter_login_window_problem (GdmGreeterLoginWindow *login_window,
         return TRUE;
 }
 
+gboolean
+gdm_greeter_login_window_service_unavailable (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: service unavailable: %s", service_name);
+
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task != NULL) {
+                gdm_task_list_remove_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           task);
+                g_object_unref (task);
+        }
+
+        return TRUE;
+}
+
 void
 gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_window,
                                               const char            *username,
diff --git a/gui/simple-greeter/gdm-greeter-login-window.h b/gui/simple-greeter/gdm-greeter-login-window.h
index c312a47..041cbc4 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.h
+++ b/gui/simple-greeter/gdm-greeter-login-window.h
@@ -94,6 +94,9 @@ gboolean            gdm_greeter_login_window_problem            (GdmGreeterLogin
                                                                  const char *service_name,
                                                                  const char *text);
 
+gboolean            gdm_greeter_login_window_service_unavailable (GdmGreeterLoginWindow *login_window,
+                                                                  const char *service_name);
+
 void               gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_window,
                                                                  const char            *username,
                                                                  int                    delay);
diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index 848ea1e..3bf24e8 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -90,6 +90,16 @@ on_problem (GdmGreeterClient  *client,
 }
 
 static void
+on_service_unavailable (GdmGreeterClient  *client,
+                        const char        *service_name,
+                        GdmGreeterSession *session)
+{
+        g_debug ("GdmGreeterSession: Service Unavailable: %s", service_name);
+
+        gdm_greeter_login_window_service_unavailable (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name);
+}
+
+static void
 on_ready (GdmGreeterClient  *client,
           const char        *service_name,
           GdmGreeterSession *session)
@@ -670,6 +680,10 @@ gdm_greeter_session_init (GdmGreeterSession *session)
                           G_CALLBACK (on_problem),
                           session);
         g_signal_connect (session->priv->client,
+                          "service-unavailable",
+                          G_CALLBACK (on_service_unavailable),
+                          session);
+        g_signal_connect (session->priv->client,
                           "ready",
                           G_CALLBACK (on_ready),
                           session);
-- 
1.7.0.1


From a00cf6e7fb82cc6bc0cfeefbdeb6303e5163d04c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 28 Oct 2009 23:55:00 -0400
Subject: [PATCH 43/47] Don't add task to UI if it's invisible

---
 gui/simple-greeter/gdm-greeter-login-window.c |   14 +++++++++++---
 1 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 03858e3..4a3df26 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -2113,6 +2113,17 @@ gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
 
         g_object_unref (actions);
 
+        name = gdm_task_get_name (GDM_TASK (extension));
+        description = gdm_task_get_description (GDM_TASK (extension));
+
+        if (!gdm_task_is_visible (GDM_TASK (extension))) {
+                g_debug ("GdmGreeterLoginWindow: new extension '%s - %s' won't be added",
+                         name, description);
+                g_free (name);
+                g_free (description);
+                return;
+        }
+
         g_signal_connect_swapped (GDM_CONVERSATION (extension),
                                   "answer",
                                   G_CALLBACK (on_conversation_answer),
@@ -2126,9 +2137,6 @@ gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
                                   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);
 
-- 
1.7.0.1


From 29c3f9d27a6e2c9c2769641eece61bf80bf3dfcb Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 29 Oct 2009 00:39:20 -0400
Subject: [PATCH 44/47] add lame check to see if fingerprint is enabled

---
 .../fingerprint/gdm-fingerprint-extension.c        |   35 +++++++++++++++++++-
 1 files changed, 34 insertions(+), 1 deletions(-)

diff --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
index e1fc0ed..b749ac1 100644
--- a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
+++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
@@ -197,7 +197,40 @@ gdm_fingerprint_extension_is_choosable (GdmTask *task)
 gboolean
 gdm_fingerprint_extension_is_visible (GdmTask *task)
 {
-        return TRUE;
+        char *contents, **lines;
+        gboolean ret;
+        guint i;
+
+        /* Stolen from gnome-about-me.
+         *
+         * FIXME: We should fix pam_fprintd to return authinfo_unavail instead of
+         * doing this distro specific hack.
+         */
+
+        if (g_file_get_contents ("/etc/sysconfig/authconfig",
+                                 &contents, NULL, NULL) == FALSE)
+                return FALSE;
+
+        lines = g_strsplit (contents, "\n", -1);
+        g_free (contents);
+
+        ret = FALSE;
+
+        for (i = 0; lines[i] ; i++) {
+                if (g_str_has_prefix (lines[i], "USEFPRINTD=") != FALSE) {
+                        char *value;
+
+                        value = lines[i] + strlen ("USEFPRINTD=");
+                        if (g_strcmp0 (value, "yes") == 0) {
+                                ret = TRUE;
+                                break;
+                        }
+                }
+        }
+
+        g_strfreev (lines);
+
+        return ret;
 }
 
 static void
-- 
1.7.0.1


From c493295a0a421dfa757bb2809aa694356ae3f4a0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 29 Oct 2009 00:10:40 -0400
Subject: [PATCH 45/47] don't activate invisible tasks

---
 gui/simple-greeter/gdm-task-list.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/gui/simple-greeter/gdm-task-list.c b/gui/simple-greeter/gdm-task-list.c
index dd77ed6..c9d7451 100644
--- a/gui/simple-greeter/gdm-task-list.c
+++ b/gui/simple-greeter/gdm-task-list.c
@@ -335,6 +335,10 @@ gdm_task_list_set_active_task (GdmTaskList *widget,
         gboolean   was_sensitive;
         gboolean   was_activated;
 
+        if (!gdm_task_is_visible (task)) {
+                return FALSE;
+        }
+
         was_sensitive = GTK_WIDGET_SENSITIVE (widget);
         gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
 
-- 
1.7.0.1


From 427844337e5690d0a750f1641a5066f96c144b15 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 29 Oct 2009 00:46:34 -0400
Subject: [PATCH 46/47] Add lame check for smart card daemon

We don't want to show the smart card bits if the daemon isn't running

This is just a temporary hack.  We need a better solution.
---
 .../plugins/smartcard/gdm-smartcard-extension.c    |   18 +++++++++++++++++-
 1 files changed, 17 insertions(+), 1 deletions(-)

diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
index 9967d5f..903e18d 100644
--- a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
+++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -312,7 +313,22 @@ gdm_smartcard_extension_is_choosable (GdmTask *task)
 gboolean
 gdm_smartcard_extension_is_visible (GdmTask *task)
 {
-        return TRUE;
+        char *contents;
+        pid_t pid;
+
+        if (g_file_get_contents ("/var/run/pcscd.pid",
+                                 &contents, NULL, NULL) == FALSE) {
+                return FALSE;
+        }
+
+        pid = (pid_t) atoi (contents);
+        g_free (contents);
+
+        if (pid == 0) {
+                return FALSE;
+        }
+
+        return kill (pid, 0);
 }
 
 static void
-- 
1.7.0.1


From 8bc988c89f9c24e59c374a1ddc4c7efd2380bd3b Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 6 Nov 2009 13:35:26 -0500
Subject: [PATCH 47/47] Don't delay login for passwd -d users

Before we'd delay login if timed login was enabled, but
we should have been checking if it was the reason login
was happening.
---
 gui/simple-greeter/gdm-greeter-login-window.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index 4a3df26..de45663 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -966,7 +966,7 @@ gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_
                  * so they can pick language/session.  Will need to refactor things
                  * a bit so we can share code with timed login.
                  */
-                if (!login_window->priv->timed_login_enabled) {
+                if (strcmp (service_name, "gdm-autologin") != 0) {
 
                         g_debug ("GdmGreeterLoginWindow: Okay, we'll start the session anyway,"
                                  "because the user isn't ever going to get an opportunity to"
-- 
1.7.0.1