From 22579594848c9ec70211304e136fb0914f8c0fda Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Mon, 13 Apr 2009 21:29:14 +0000 Subject: [PATCH] - Add less boring multistack patch for testing --- gdm-2.26.0-multistack.patch | 17980 ++++++++++++++++++++++++++++++++++ gdm-fingerprint-16.png | Bin 0 -> 461 bytes gdm-fingerprint-48.png | Bin 0 -> 1638 bytes gdm-fingerprint.pam | 15 + gdm-password.pam | 17 + gdm-smartcard-16.png | Bin 0 -> 871 bytes gdm-smartcard-48.png | Bin 0 -> 4202 bytes gdm-smartcard.pam | 15 + gdm.spec | 57 +- 9 files changed, 18077 insertions(+), 7 deletions(-) create mode 100644 gdm-2.26.0-multistack.patch create mode 100644 gdm-fingerprint-16.png create mode 100644 gdm-fingerprint-48.png create mode 100644 gdm-fingerprint.pam create mode 100644 gdm-password.pam create mode 100644 gdm-smartcard-16.png create mode 100644 gdm-smartcard-48.png create mode 100644 gdm-smartcard.pam diff --git a/gdm-2.26.0-multistack.patch b/gdm-2.26.0-multistack.patch new file mode 100644 index 0000000..b46265a --- /dev/null +++ b/gdm-2.26.0-multistack.patch @@ -0,0 +1,17980 @@ +From 9376a6f3e7fcd139f50dcf0d8c91ce93c8215ec6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Feb 2009 13:52:19 -0500 +Subject: [PATCH 01/33] Add a comment marking protected api in chooser + +The chooser widget has methods that only its +subclasses are supposed to call. We should +mark them as such. +--- + gui/simple-greeter/gdm-chooser-widget.h | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +diff --git a/gui/simple-greeter/gdm-chooser-widget.h b/gui/simple-greeter/gdm-chooser-widget.h +index 578e613..7e3e59c 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); + ++/* Protected ++ */ + void gdm_chooser_widget_loaded (GdmChooserWidget *widget); + + G_END_DECLS +-- +1.6.2.2 + + +From 73b2c5ea212dcd38a5c825f264261138edb1f03d Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 6 Feb 2009 17:44:37 -0500 +Subject: [PATCH 02/33] Drop duplicated entry introspection output + +--- + daemon/gdm-greeter-server.c | 1 - + 1 files changed, 0 insertions(+), 1 deletions(-) + +diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c +index 2e01d33..cecce83 100644 +--- a/daemon/gdm-greeter-server.c ++++ b/daemon/gdm-greeter-server.c +@@ -752,7 +752,6 @@ do_introspect (DBusConnection *connection, + " \n" + " \n" + " \n" +- " \n" + " \n" + " \n" + " \n" +-- +1.6.2.2 + + +From c785ddc8f889a566dc247847721a19e88ad9a88f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Mar 2009 10:07:03 -0500 +Subject: [PATCH 03/33] Make lookup_item not warn when passing NULL for args + +gtk_tree_model_get doesn't like NULL, and we allow +NULL for optional return values. + +We now check each argument for NULL and call +gtk_tree_model_get individually N times (one for +each argument) instead of just once. +--- + gui/simple-greeter/gdm-chooser-widget.c | 31 ++++++++++++++++++++++++------- + 1 files changed, 24 insertions(+), 7 deletions(-) + +diff --git a/gui/simple-greeter/gdm-chooser-widget.c b/gui/simple-greeter/gdm-chooser-widget.c +index f0298c6..b1ba9b4 100644 +--- a/gui/simple-greeter/gdm-chooser-widget.c ++++ b/gui/simple-greeter/gdm-chooser-widget.c +@@ -2132,13 +2132,30 @@ gdm_chooser_widget_lookup_item (GdmChooserWidget *widget, + } + g_free (active_item_id); + +- gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter, +- CHOOSER_IMAGE_COLUMN, image, +- CHOOSER_NAME_COLUMN, name, +- CHOOSER_PRIORITY_COLUMN, priority, +- CHOOSER_ITEM_IS_IN_USE_COLUMN, is_in_use, +- CHOOSER_ITEM_IS_SEPARATED_COLUMN, is_separate, +- -1); ++ if (image != NULL) { ++ gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter, ++ CHOOSER_IMAGE_COLUMN, image, -1); ++ } ++ ++ if (name != NULL) { ++ gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter, ++ CHOOSER_NAME_COLUMN, name, -1); ++ } ++ ++ if (priority != NULL) { ++ gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter, ++ CHOOSER_PRIORITY_COLUMN, priority, -1); ++ } ++ ++ if (is_in_use != NULL) { ++ gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter, ++ CHOOSER_ITEM_IS_IN_USE_COLUMN, is_in_use, -1); ++ } ++ ++ if (is_separate != NULL) { ++ gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter, ++ CHOOSER_ITEM_IS_SEPARATED_COLUMN, is_separate, -1); ++ } + + return TRUE; + } +-- +1.6.2.2 + + +From d888673141c9b947ccada6293600765f618c2ef5 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 6 Feb 2009 15:35:00 -0500 +Subject: [PATCH 04/33] Drop "stopped" signal from worker-job class + +It was unused, dead code. +--- + daemon/gdm-session-direct.c | 14 -------------- + daemon/gdm-session-worker-job.c | 11 ----------- + daemon/gdm-session-worker-job.h | 1 - + 3 files changed, 0 insertions(+), 26 deletions(-) + +diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c +index 34a126f..3942ec7 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -1548,13 +1548,6 @@ gdm_session_direct_init (GdmSessionDirect *session) + } + + static void +-worker_stopped (GdmSessionWorkerJob *job, +- GdmSessionDirect *session) +-{ +- g_debug ("GdmSessionDirect: Worker job stopped"); +-} +- +-static void + worker_started (GdmSessionWorkerJob *job, + GdmSessionDirect *session) + { +@@ -1605,10 +1598,6 @@ start_worker (GdmSessionDirect *session) + session->priv->job = gdm_session_worker_job_new (); + gdm_session_worker_job_set_server_address (session->priv->job, session->priv->server_address); + g_signal_connect (session->priv->job, +- "stopped", +- G_CALLBACK (worker_stopped), +- session); +- g_signal_connect (session->priv->job, + "started", + G_CALLBACK (worker_started), + session); +@@ -1630,9 +1619,6 @@ static void + stop_worker (GdmSessionDirect *session) + { + g_signal_handlers_disconnect_by_func (session->priv->job, +- G_CALLBACK (worker_stopped), +- session); +- g_signal_handlers_disconnect_by_func (session->priv->job, + G_CALLBACK (worker_started), + session); + g_signal_handlers_disconnect_by_func (session->priv->job, +diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c +index 6723464..633d6e2 100644 +--- a/daemon/gdm-session-worker-job.c ++++ b/daemon/gdm-session-worker-job.c +@@ -68,7 +68,6 @@ enum { + + enum { + STARTED, +- STOPPED, + EXITED, + DIED, + LAST_SIGNAL +@@ -390,16 +389,6 @@ gdm_session_worker_job_class_init (GdmSessionWorkerJobClass *klass) + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +- signals [STOPPED] = +- g_signal_new ("stopped", +- G_OBJECT_CLASS_TYPE (object_class), +- G_SIGNAL_RUN_FIRST, +- G_STRUCT_OFFSET (GdmSessionWorkerJobClass, stopped), +- NULL, +- NULL, +- g_cclosure_marshal_VOID__VOID, +- G_TYPE_NONE, +- 0); + signals [EXITED] = + g_signal_new ("exited", + G_OBJECT_CLASS_TYPE (object_class), +diff --git a/daemon/gdm-session-worker-job.h b/daemon/gdm-session-worker-job.h +index d42eb37..5ad1c92 100644 +--- a/daemon/gdm-session-worker-job.h ++++ b/daemon/gdm-session-worker-job.h +@@ -46,7 +46,6 @@ typedef struct + GObjectClass parent_class; + + void (* started) (GdmSessionWorkerJob *session_worker_job); +- void (* stopped) (GdmSessionWorkerJob *session_worker_job); + void (* exited) (GdmSessionWorkerJob *session_worker_job, + int exit_code); + +-- +1.6.2.2 + + +From d0e4d6a9602b519b2ce991aaabc1dc0b102aca96 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 16 Jan 2009 11:00:08 -0500 +Subject: [PATCH 05/33] Drop session "Open" in favor of "StartConversation" + +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 | 13 +- + daemon/gdm-product-slave.c | 47 ++++-- + daemon/gdm-session-direct.c | 327 ++++++++++++++++++++++++--------------- + daemon/gdm-session-private.h | 3 +- + daemon/gdm-session-relay.c | 29 +++-- + daemon/gdm-session-worker-job.c | 7 + + daemon/gdm-session-worker-job.h | 2 + + daemon/gdm-session.c | 20 ++- + daemon/gdm-session.h | 9 +- + daemon/gdm-simple-slave.c | 3 - + daemon/test-session.c | 13 +- + 11 files changed, 296 insertions(+), 177 deletions(-) + +diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c +index d09c913..6497293 100644 +--- a/daemon/gdm-factory-slave.c ++++ b/daemon/gdm-factory-slave.c +@@ -180,10 +180,11 @@ on_session_secret_info_query (GdmSession *session, + } + + static void +-on_session_opened (GdmSession *session, +- GdmFactorySlave *slave) ++on_session_conversation_started (GdmSession *session, ++ GdmFactorySlave *slave, ++ const char *service_name) + { +- g_debug ("GdmFactorySlave: session opened"); ++ g_debug ("GdmFactorySlave: session conversation started"); + + gdm_greeter_server_ready (slave->priv->greeter_server); + } +@@ -367,7 +368,7 @@ on_session_relay_connected (GdmSessionRelay *session, + { + g_debug ("GdmFactorySlave: Relay Connected"); + +- gdm_session_open (GDM_SESSION (slave->priv->session)); ++ gdm_session_start_conversation (GDM_SESSION (slave->priv->session), "gdm"); + } + + static void +@@ -694,8 +695,8 @@ gdm_factory_slave_start (GdmSlave *slave) + + GDM_FACTORY_SLAVE (slave)->priv->session = gdm_session_relay_new (); + g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session, +- "opened", +- G_CALLBACK (on_session_opened), ++ "conversation-started", ++ G_CALLBACK (on_session_conversation_started), + slave); + g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session, + "setup-complete", +diff --git a/daemon/gdm-product-slave.c b/daemon/gdm-product-slave.c +index 9adcb09..15a2820 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_opened (GdmProductSlave *slave) ++relay_session_conversation_started (GdmProductSlave *slave, ++ const char *service_name) + { +- send_dbus_void_method (slave->priv->session_relay_connection, +- "Opened"); ++ send_dbus_string_method (slave->priv->session_relay_connection, ++ "ConversationStarted", service_name); + } + + static void +-on_session_opened (GdmSession *session, +- GdmProductSlave *slave) ++on_session_conversation_started (GdmSession *session, ++ const char *service_name, ++ GdmProductSlave *slave) + { +- g_debug ("GdmProductSlave: session opened"); ++ g_debug ("GdmProductSlave: session conversation started"); + +- relay_session_opened (slave); ++ relay_session_conversation_started (slave, service_name); + } + + static void +@@ -784,10 +786,27 @@ on_relay_user_selected (GdmProductSlave *slave, + } + + static void +-on_relay_open (GdmProductSlave *slave, +- DBusMessage *message) ++on_relay_start_conversation (GdmProductSlave *slave, ++ DBusMessage *message) + { +- gdm_session_open (GDM_SESSION (slave->priv->session)); ++ DBusError error; ++ char *service_name; ++ dbus_bool_t res; ++ ++ dbus_error_init (&error); ++ res = dbus_message_get_args (message, ++ &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID); ++ if (res) { ++ g_debug ("GdmProductSlave: Started conversation with %s service", service_name); ++ gdm_session_start_conversation (GDM_SESSION (slave->priv->session), ++ service_name); ++ } else { ++ g_warning ("Unable to get arguments: %s", error.message); ++ } ++ ++ dbus_error_free (&error); + } + + static void +@@ -832,8 +851,8 @@ create_new_session (GdmProductSlave *slave) + g_free (display_device); + + g_signal_connect (slave->priv->session, +- "opened", +- G_CALLBACK (on_session_opened), ++ "conversation-started", ++ G_CALLBACK (on_session_conversation_started), + slave); + g_signal_connect (slave->priv->session, + "setup-complete", +@@ -991,8 +1010,8 @@ relay_dbus_handle_message (DBusConnection *connection, + on_relay_user_selected (slave, message); + } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "StartSession")) { + on_relay_start_session (slave, message); +- } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "Open")) { +- on_relay_open (slave, message); ++ } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "StartConversation")) { ++ on_relay_start_conversation (slave, message); + } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "Cancelled")) { + on_relay_cancelled (slave, message); + } else { +diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c +index 3942ec7..304a67b 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -61,6 +61,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 */ +@@ -73,13 +83,11 @@ struct _GdmSessionDirectPrivate + char *selected_user; + char *user_x11_authority_file; + +- DBusMessage *message_pending_reply; +- DBusConnection *worker_connection; ++ GdmSessionConversation *conversation; + +- GdmSessionWorkerJob *job; +- GPid session_pid; + guint32 is_authenticated : 1; + guint32 is_running : 1; ++ GPid session_pid; + + /* object lifetime scope */ + char *id; +@@ -116,39 +124,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, +@@ -157,7 +165,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); + } + +@@ -165,18 +173,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); + } + +@@ -188,22 +196,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 +@@ -211,11 +229,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 +@@ -223,6 +246,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, +@@ -731,25 +755,26 @@ 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); ++ conversation = session->priv->conversation; ++ 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 +@@ -758,27 +783,29 @@ answer_pending_query (GdmSessionDirect *session, + { + DBusMessage *reply; + DBusMessageIter iter; ++ GdmSessionConversation *conversation; + +- g_assert (session->priv->message_pending_reply != NULL); ++ conversation = find_conversation_by_name (session, service_name); + +- reply = dbus_message_new_method_return (session->priv->message_pending_reply); ++ reply = dbus_message_new_method_return (conversation->message_pending_reply); + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &answer); + +- dbus_connection_send (session->priv->worker_connection, reply, NULL); ++ conversation = session->priv->conversation; ++ 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 +@@ -796,7 +823,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); +@@ -819,7 +846,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); +@@ -861,7 +888,7 @@ gdm_session_direct_handle_cancel_pending_query (GdmSessionDirect *session, + DBusMessage *reply; + + g_debug ("GdmSessionDirect: worker cancelling pending query"); +- cancel_pending_query (session); ++ cancel_pending_query (conversation); + + reply = dbus_message_new_method_return (message); + dbus_connection_send (connection, reply, NULL); +@@ -1439,16 +1466,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); + +@@ -1465,8 +1494,9 @@ handle_connection (DBusServer *server, + &vtable, + session); + +- g_debug ("GdmSessionDirect: Emitting opened signal"); +- _gdm_session_opened (GDM_SESSION (session)); ++ g_debug ("GdmSessionDirect: Emitting conversation-started signal"); ++ _gdm_session_conversation_started (GDM_SESSION (session), ++ conversation->service_name); + } + } + +@@ -1536,8 +1566,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, +@@ -1549,7 +1577,7 @@ gdm_session_direct_init (GdmSessionDirect *session) + + static void + worker_started (GdmSessionWorkerJob *job, +- GdmSessionDirect *session) ++ GdmSessionConversation *conversation) + { + g_debug ("GdmSessionDirect: Worker job started"); + } +@@ -1557,99 +1585,119 @@ worker_started (GdmSessionWorkerJob *job, + static void + worker_exited (GdmSessionWorkerJob *job, + int code, +- GdmSessionDirect *session) ++ GdmSessionConversation *conversation) + { + g_debug ("GdmSessionDirect: Worker job exited: %d", code); + +- if (!session->priv->is_authenticated) { ++ if (!conversation->session->priv->is_authenticated) { + char *msg; + + msg = g_strdup_printf (_("worker exited with status %d"), code); +- _gdm_session_authentication_failed (GDM_SESSION (session), msg); ++ _gdm_session_authentication_failed (GDM_SESSION (conversation->session), msg); + g_free (msg); +- } else if (session->priv->is_running) { +- _gdm_session_session_exited (GDM_SESSION (session), code); ++ } else if (conversation->session->priv->is_running) { ++ _gdm_session_session_exited (GDM_SESSION (conversation->session), code); + } + } + + static void + worker_died (GdmSessionWorkerJob *job, + int signum, +- GdmSessionDirect *session) ++ GdmSessionConversation *conversation) + { + g_debug ("GdmSessionDirect: Worker job died: %d", signum); + +- if (!session->priv->is_authenticated) { ++ if (!conversation->session->priv->is_authenticated) { + char *msg; + + msg = g_strdup_printf (_("worker exited with status %d"), signum); +- _gdm_session_authentication_failed (GDM_SESSION (session), msg); ++ _gdm_session_authentication_failed (GDM_SESSION (conversation->session), msg); + g_free (msg); +- } else if (session->priv->is_running) { +- _gdm_session_session_died (GDM_SESSION (session), signum); ++ } else if (conversation->session->priv->is_running) { ++ _gdm_session_session_died (GDM_SESSION (conversation->session), signum); + } + } + +-static gboolean +-start_worker (GdmSessionDirect *session) ++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); + +- 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_remove_filter (conversation->worker_connection, on_message, session); ++ ++ 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_open (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: Opening session"); ++ g_debug ("GdmSessionDirect: starting conversation"); + +- start_worker (impl); ++ impl->priv->conversation = start_conversation (impl, service_name); + } + + static void +@@ -1662,6 +1710,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); + +@@ -1699,7 +1748,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"); + } + +@@ -1717,6 +1767,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); + +@@ -1760,7 +1811,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"); + } + +@@ -1774,7 +1826,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); +@@ -1788,7 +1841,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); +@@ -1801,22 +1855,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 +@@ -1824,16 +1884,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 (); +@@ -1847,6 +1910,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, +@@ -1856,7 +1920,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"); + } + +@@ -1986,6 +2051,7 @@ static void + gdm_session_direct_start_session (GdmSession *session) + { + GdmSessionDirect *impl = GDM_SESSION_DIRECT (session); ++ GdmSessionConversation *conversation; + char *command; + char *program; + +@@ -1999,7 +2065,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); + } + +@@ -2012,16 +2079,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); +@@ -2050,7 +2113,6 @@ gdm_session_direct_close (GdmSession *session) + + g_hash_table_remove_all (impl->priv->environment); + +- impl->priv->session_pid = -1; + impl->priv->is_authenticated = FALSE; + impl->priv->is_running = FALSE; + } +@@ -2070,10 +2132,19 @@ static void + gdm_session_direct_cancel (GdmSession *session) + { + GdmSessionDirect *impl = GDM_SESSION_DIRECT (session); ++ GHashTableIter iter; ++ gpointer key, value; + + g_return_if_fail (session != NULL); + +- cancel_pending_query (impl); ++ g_hash_table_iter_init (&iter, impl->priv->conversations); ++ while (g_hash_table_iter_next (&iter, &key, &value)) { ++ GdmSessionConversation *conversation; ++ ++ conversation = (GdmSessionConversation *) value; ++ ++ cancel_pending_query (conversation); ++ } + } + + char * +@@ -2089,6 +2160,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); + +@@ -2098,7 +2170,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)); + } + +@@ -2107,6 +2180,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); + +@@ -2116,7 +2190,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)); + } + +@@ -2125,6 +2200,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); + +@@ -2134,7 +2210,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)); + } + +@@ -2394,7 +2471,7 @@ gdm_session_direct_constructor (GType type, + static void + gdm_session_iface_init (GdmSessionIface *iface) + { +- iface->open = gdm_session_direct_open; ++ iface->start_conversation = gdm_session_direct_start_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 6a6fcfc..074aa6f 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_opened (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 b57bf89..35b4738 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_open (GdmSession *session) ++gdm_session_relay_start_conversation (GdmSession *session, ++ const char *service_name) + { + GdmSessionRelay *impl = GDM_SESSION_RELAY (session); +- send_dbus_void_signal (impl, "Open"); ++ send_dbus_string_signal (impl, "StartConversation", service_name); + } + + static void +@@ -664,22 +665,28 @@ handle_session_stopped (GdmSessionRelay *session_relay, + } + + static DBusHandlerResult +-handle_opened (GdmSessionRelay *session_relay, ++handle_conversation_started (GdmSessionRelay *session_relay, + DBusConnection *connection, + DBusMessage *message) + { + DBusMessage *reply; + DBusError error; ++ char *service_name; + + dbus_error_init (&error); ++ if (! dbus_message_get_args (message, &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID)) { ++ g_warning ("ERROR: %s", error.message); ++ } + +- g_debug ("GdmSessionRelay: Opened"); ++ g_debug ("GdmSessionRelay: Conversation Started"); + + reply = dbus_message_new_method_return (message); + dbus_connection_send (connection, reply, NULL); + dbus_message_unref (reply); + +- _gdm_session_opened (GDM_SESSION (session_relay)); ++ _gdm_session_conversation_started (GDM_SESSION (session_relay), service_name); + + return DBUS_HANDLER_RESULT_HANDLED; + } +@@ -719,8 +726,8 @@ session_handle_child_message (DBusConnection *connection, + return handle_session_started (session_relay, connection, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionStopped")) { + return handle_session_stopped (session_relay, connection, message); +- } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "Opened")) { +- return handle_opened (session_relay, connection, message); ++ } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "ConversationStarted")) { ++ return handle_conversation_started (session_relay, connection, message); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +@@ -749,7 +756,8 @@ do_introspect (DBusConnection *connection, + /* interface */ + xml = g_string_append (xml, + " \n" +- " \n" ++ " \n" ++ " \n" + " \n" + " \n" + " \n" +@@ -810,7 +818,8 @@ do_introspect (DBusConnection *connection, + " \n" + " \n" + +- " \n" ++ " \n" ++ " \n" + " \n" + " \n" + " \n" +@@ -1106,7 +1115,7 @@ static void + gdm_session_iface_init (GdmSessionIface *iface) + { + +- iface->open = gdm_session_relay_open; ++ iface->start_conversation = gdm_session_relay_start_conversation; + iface->setup = gdm_session_relay_setup; + iface->setup_for_user = gdm_session_relay_setup_for_user; + iface->authenticate = gdm_session_relay_authenticate; +diff --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 feb7938..0073294 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -28,7 +28,7 @@ + #include "gdm-session-private.h" + + enum { +- OPENED = 0, ++ CONVERSATION_STARTED = 0, + SETUP_COMPLETE, + SETUP_FAILED, + RESET_COMPLETE, +@@ -78,11 +78,12 @@ gdm_session_get_type (void) + } + + void +-gdm_session_open (GdmSession *session) ++gdm_session_start_conversation (GdmSession *session, ++ const char *service_name) + { + g_return_if_fail (GDM_IS_SESSION (session)); + +- GDM_SESSION_GET_IFACE (session)->open (session); ++ GDM_SESSION_GET_IFACE (session)->start_conversation (session, service_name); + } + + void +@@ -203,14 +204,14 @@ gdm_session_class_init (gpointer g_iface) + { + GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + +- signals [OPENED] = +- g_signal_new ("opened", ++ signals [CONVERSATION_STARTED] = ++ g_signal_new ("conversation-started", + iface_type, + G_SIGNAL_RUN_FIRST, +- G_STRUCT_OFFSET (GdmSessionIface, opened), ++ G_STRUCT_OFFSET (GdmSessionIface, conversation_started), + NULL, + NULL, +- g_cclosure_marshal_VOID__VOID, ++ g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 0); + signals [SETUP_COMPLETE] = +@@ -608,10 +609,11 @@ _gdm_session_session_died (GdmSession *session, + } + + void +-_gdm_session_opened (GdmSession *session) ++_gdm_session_conversation_started (GdmSession *session, ++ const char *service_name) + { + g_return_if_fail (GDM_IS_SESSION (session)); +- g_signal_emit (session, signals [OPENED], 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 dfb7e27..77e0cf6 100644 +--- a/daemon/gdm-session.h ++++ b/daemon/gdm-session.h +@@ -45,7 +45,8 @@ struct _GdmSessionIface + GTypeInterface base_iface; + + /* Methods */ +- void (* open) (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, +@@ -103,7 +104,8 @@ struct _GdmSessionIface + int exit_code); + void (* session_died) (GdmSession *session, + int signal_number); +- void (* opened) (GdmSession *session); ++ void (* conversation_started) (GdmSession *session, ++ const char *service_name); + void (* closed) (GdmSession *session); + void (* selected_user_changed) (GdmSession *session, + const char *text); +@@ -118,7 +120,8 @@ struct _GdmSessionIface + + GType gdm_session_get_type (void) G_GNUC_CONST; + +-void gdm_session_open (GdmSession *session); ++void gdm_session_start_conversation (GdmSession *session, ++ const char *service_name); + void gdm_session_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 3a2623c..f193ff5 100644 +--- a/daemon/gdm-simple-slave.c ++++ b/daemon/gdm-simple-slave.c +@@ -174,7 +174,6 @@ reset_session (GdmSimpleSlave *slave) + { + destroy_session (slave); + create_new_session (slave); +- gdm_session_open (GDM_SESSION (slave->priv->session)); + } + + static gboolean +@@ -816,8 +815,6 @@ on_greeter_connected (GdmGreeterServer *greeter_server, + + g_debug ("GdmSimpleSlave: Greeter connected"); + +- gdm_session_open (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 c6a158c..d9fa26e 100644 +--- a/daemon/test-session.c ++++ b/daemon/test-session.c +@@ -33,12 +33,13 @@ + static GMainLoop *loop; + + static void +-on_open (GdmSession *session, +- const char *username) ++on_conversation_started (GdmSession *session, ++ const char *service_name, ++ const char *username) + { + g_debug ("Got opened: calling setup..."); + +- gdm_session_setup (session, "gdm"); ++ gdm_session_setup (session, service_name); + } + + static void +@@ -256,11 +257,11 @@ main (int argc, + username = argv[1]; + } + +- gdm_session_open (GDM_SESSION (session)); ++ gdm_session_start_conversation (GDM_SESSION (session), "gdm"); + + g_signal_connect (session, +- "opened", +- G_CALLBACK (on_open), ++ "conversation-started", ++ G_CALLBACK (on_conversation_started), + username); + g_signal_connect (session, + "setup-complete", +-- +1.6.2.2 + + +From b79fde5ef2f6c41a516b78dc0666b212ba537273 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 4 Feb 2009 10:55:03 -0500 +Subject: [PATCH 06/33] 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 304a67b..0a71512 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -1623,6 +1623,7 @@ start_conversation (GdmSessionDirect *session, + const char *service_name) + { + GdmSessionConversation *conversation; ++ char *job_name; + + conversation = g_new0 (GdmSessionConversation, 1); + conversation->session = session; +@@ -1643,12 +1644,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.6.2.2 + + +From d51d5c4ed394b68eaeaccd43f2556a2729584774 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 16 Jan 2009 13:01:48 -0500 +Subject: [PATCH 07/33] 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 | 32 ++++++++++++++---- + 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, 99 insertions(+), 20 deletions(-) + +diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c +index 6497293..d1bb5dd 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, + " \n" ++ " \n" ++ " \n" ++ " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" +@@ -759,6 +795,7 @@ do_introspect (DBusConnection *connection, + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \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 f193ff5..732c085 100644 +--- a/daemon/gdm-simple-slave.c ++++ b/daemon/gdm-simple-slave.c +@@ -478,17 +478,19 @@ on_session_secret_info_query (GdmSession *session, + } + + static void +-on_session_opened (GdmSession *session, +- GdmSimpleSlave *slave) ++on_session_conversation_started (GdmSession *session, ++ const char *service_name, ++ GdmSimpleSlave *slave) + { + gboolean res; + gboolean enabled; + char *username; + int delay; + +- g_debug ("GdmSimpleSlave: session opened"); ++ g_debug ("GdmSimpleSlave: conversation started"); + if (slave->priv->greeter_server != NULL) { +- res = gdm_greeter_server_ready (slave->priv->greeter_server); ++ res = gdm_greeter_server_ready (slave->priv->greeter_server, ++ service_name); + if (! res) { + g_warning ("Unable to send ready"); + } +@@ -504,8 +506,10 @@ on_session_opened (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); + } + +@@ -597,8 +601,8 @@ create_new_session (GdmSimpleSlave *slave) + g_free (display_hostname); + + g_signal_connect (slave->priv->session, +- "opened", +- G_CALLBACK (on_session_opened), ++ "conversation-started", ++ G_CALLBACK (on_session_conversation_started), + slave); + g_signal_connect (slave->priv->session, + "setup-complete", +@@ -729,6 +733,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) + { +@@ -903,6 +917,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 461b85c..1e2c55d 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 17afb4e..40d5e1f 100644 +--- a/gui/simple-greeter/gdm-greeter-session.c ++++ b/gui/simple-greeter/gdm-greeter-session.c +@@ -84,6 +84,7 @@ on_problem (GdmGreeterClient *client, + + static void + on_ready (GdmGreeterClient *client, ++ const char *service_name, + GdmGreeterSession *session) + { + g_debug ("GdmGreeterSession: Ready"); +@@ -251,6 +252,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 +@@ -413,6 +415,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.6.2.2 + + +From a9dfd48535ac44d93dbdaa383c4de03b5d76f3a4 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 16 Jan 2009 15:18:31 -0500 +Subject: [PATCH 08/33] 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 | 132 ++++++++++++++++++++++++++++++++++--------- + 1 files changed, 106 insertions(+), 26 deletions(-) + +diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c +index 0a71512..4bb14f4 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -84,6 +84,7 @@ struct _GdmSessionDirectPrivate + char *user_x11_authority_file; + + GdmSessionConversation *conversation; ++ GHashTable *conversations; + + guint32 is_authenticated : 1; + guint32 is_running : 1; +@@ -1460,6 +1461,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, +@@ -1467,10 +1504,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, +@@ -1543,6 +1592,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, +@@ -1566,6 +1626,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, +@@ -1677,8 +1742,6 @@ stop_conversation (GdmSessionConversation *conversation) + G_CALLBACK (worker_died), + conversation); + +- cancel_pending_query (session); +- + if (conversation->worker_connection != NULL) { + dbus_connection_remove_filter (conversation->worker_connection, on_message, session); + +@@ -1687,9 +1750,9 @@ stop_conversation (GdmSessionConversation *conversation) + } + + 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 +@@ -1697,12 +1760,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 +@@ -1753,8 +1824,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"); + } + +@@ -1816,8 +1887,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"); + } + +@@ -1831,8 +1902,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); +@@ -1846,8 +1915,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); +@@ -2076,6 +2143,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); +@@ -2092,6 +2181,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; + +@@ -2136,20 +2227,9 @@ gdm_session_direct_answer_query (GdmSession *session, + static void + gdm_session_direct_cancel (GdmSession *session) + { +- GdmSessionDirect *impl = GDM_SESSION_DIRECT (session); +- GHashTableIter iter; +- gpointer key, value; +- + g_return_if_fail (session != NULL); + +- g_hash_table_iter_init (&iter, impl->priv->conversations); +- while (g_hash_table_iter_next (&iter, &key, &value)) { +- GdmSessionConversation *conversation; +- +- conversation = (GdmSessionConversation *) value; +- +- cancel_pending_query (conversation); +- } ++ stop_all_conversations (GDM_SESSION_DIRECT (session)); + } + + char * +-- +1.6.2.2 + + +From 20255effdda49485f61927d78cdb27abf19d6779 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 4 Mar 2009 22:09:21 -0500 +Subject: [PATCH 09/33] 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 732c085..30faaa6 100644 +--- a/daemon/gdm-simple-slave.c ++++ b/daemon/gdm-simple-slave.c +@@ -1030,8 +1030,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.6.2.2 + + +From 05215925c9946ea7011ef7281e9dab34026523e9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 22 Jan 2009 08:52:01 -0500 +Subject: [PATCH 10/33] Propagate service name to more layers + +This is more prep work to get multiple concurrent +PAM stacks going. +--- + daemon/gdm-factory-slave.c | 63 +++-- + daemon/gdm-greeter-server.c | 120 ++++++-- + daemon/gdm-greeter-server.h | 17 +- + daemon/gdm-product-slave.c | 233 ++++++++++++--- + daemon/gdm-session-direct.c | 481 ++++++++++++++++++------------ + daemon/gdm-session-private.h | 22 ++- + daemon/gdm-session-relay.c | 122 ++++++-- + daemon/gdm-session-worker.c | 27 ++ + daemon/gdm-session.c | 151 ++++++---- + daemon/gdm-session.h | 44 ++- + daemon/gdm-simple-slave.c | 66 +++-- + 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, 1132 insertions(+), 451 deletions(-) + +diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c +index d1bb5dd..826612e 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,39 +257,42 @@ 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_start_session (session); ++ gdm_session_start_session (session, service_name); + + gdm_greeter_server_reset (slave->priv->greeter_server); + } + + static void + on_session_accreditation_failed (GdmSession *session, ++ const char *service_name, + const char *message, + GdmFactorySlave *slave) + { + g_debug ("GdmFactorySlave: could not successfully authenticate user: %s", + message); + +- gdm_greeter_server_problem (slave->priv->greeter_server, _("Unable to establish credentials")); ++ gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to establish credentials")); + + queue_greeter_reset (slave); + } +@@ -368,37 +380,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 +@@ -495,6 +518,10 @@ run_greeter (GdmFactorySlave *slave) + + slave->priv->greeter_server = gdm_greeter_server_new (display_id); + g_signal_connect (slave->priv->greeter_server, ++ "start-conversation", ++ G_CALLBACK (on_greeter_start_conversation), ++ slave); ++ g_signal_connect (slave->priv->greeter_server, + "begin-verification", + G_CALLBACK (on_greeter_begin_verification), + slave); +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 + #include + ++#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, + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" +@@ -764,18 +830,23 @@ do_introspect (DBusConnection *connection, + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \n" + " \n" +@@ -800,6 +871,7 @@ do_introspect (DBusConnection *connection, + " \n" + " \n" + " \n" ++ " \n" + " \n" + " \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 15a2820..dd2e1bc 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,96 +573,112 @@ 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_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 +@@ -658,36 +739,92 @@ static void + on_relay_authenticate (GdmProductSlave *slave, + DBusMessage *message) + { +- g_debug ("GdmProductSlave: Relay Authenticate"); ++ DBusError error; ++ char *service_name; ++ dbus_bool_t res; + +- gdm_session_authenticate (GDM_SESSION (slave->priv->session)); ++ dbus_error_init (&error); ++ res = dbus_message_get_args (message, ++ &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID); ++ if (res) { ++ g_debug ("GdmProductSlave: Relay Authenticate"); ++ gdm_session_authenticate (GDM_SESSION (slave->priv->session), service_name); ++ } else { ++ g_warning ("Unable to get arguments: %s", error.message); ++ dbus_error_free (&error); ++ } ++ dbus_error_free (&error); + } + + static void + on_relay_authorize (GdmProductSlave *slave, + DBusMessage *message) + { +- g_debug ("GdmProductSlave: Relay Authorize"); ++ DBusError error; ++ char *service_name; ++ dbus_bool_t res; + +- gdm_session_authorize (GDM_SESSION (slave->priv->session)); ++ dbus_error_init (&error); ++ res = dbus_message_get_args (message, ++ &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID); ++ if (res) { ++ g_debug ("GdmProductSlave: Relay Authorize"); ++ gdm_session_authorize (GDM_SESSION (slave->priv->session), service_name); ++ } else { ++ g_warning ("Unable to get arguments: %s", error.message); ++ dbus_error_free (&error); ++ } ++ dbus_error_free (&error); + } + + static void + on_relay_establish_credentials (GdmProductSlave *slave, + DBusMessage *message) + { +- g_debug ("GdmProductSlave: Relay EstablishCredentials"); ++ DBusError error; ++ char *service_name; ++ dbus_bool_t res; + +- gdm_session_accredit (GDM_SESSION (slave->priv->session), GDM_SESSION_CRED_ESTABLISH); ++ dbus_error_init (&error); ++ res = dbus_message_get_args (message, ++ &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID); ++ if (res) { ++ g_debug ("GdmProductSlave: Relay EstablishCredentials"); ++ gdm_session_accredit (GDM_SESSION (slave->priv->session), service_name, GDM_SESSION_CRED_ESTABLISH); ++ } else { ++ g_warning ("Unable to get arguments: %s", error.message); ++ dbus_error_free (&error); ++ } ++ dbus_error_free (&error); + } + + static void + on_relay_refresh_credentials (GdmProductSlave *slave, + DBusMessage *message) + { +- g_debug ("GdmProductSlave: Relay RefreshCredentials"); ++ DBusError error; ++ char *service_name; ++ dbus_bool_t res; + +- gdm_session_accredit (GDM_SESSION (slave->priv->session), GDM_SESSION_CRED_REFRESH); ++ dbus_error_init (&error); ++ res = dbus_message_get_args (message, ++ &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID); ++ if (res) { ++ g_debug ("GdmProductSlave: Relay RefreshCredentials"); ++ gdm_session_accredit (GDM_SESSION (slave->priv->session), service_name, GDM_SESSION_CRED_REFRESH); ++ } else { ++ g_warning ("Unable to get arguments: %s", error.message); ++ dbus_error_free (&error); ++ } ++ dbus_error_free (&error); + } + + static void +@@ -696,16 +833,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); +@@ -813,7 +952,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 4bb14f4..0f7e44e 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -83,9 +83,10 @@ struct _GdmSessionDirectPrivate + char *selected_user; + char *user_x11_authority_file; + +- GdmSessionConversation *conversation; + GHashTable *conversations; + ++ GList *pending_connections; ++ + guint32 is_authenticated : 1; + guint32 is_running : 1; + GPid session_pid; +@@ -192,14 +193,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, +@@ -210,12 +227,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, +@@ -227,12 +245,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, +@@ -257,7 +276,7 @@ on_session_exited (GdmSession *session, + + static DBusHandlerResult + gdm_session_direct_handle_setup_complete (GdmSessionDirect *session, +- DBusConnection *connection, ++ GdmSessionConversation *conversation, + DBusMessage *message) + { + DBusMessage *reply; +@@ -265,17 +284,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; +@@ -290,12 +309,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), NULL); ++ _gdm_session_setup_failed (GDM_SESSION (session), conversation->service_name, NULL); + + return DBUS_HANDLER_RESULT_HANDLED; + } +@@ -303,7 +322,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; +@@ -311,7 +330,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)); +@@ -321,7 +340,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; +@@ -336,7 +355,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"); +@@ -348,7 +367,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; +@@ -356,17 +375,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; +@@ -381,19 +400,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), NULL); ++ _gdm_session_authentication_failed (GDM_SESSION (session), conversation->service_name, NULL); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + static DBusHandlerResult + gdm_session_direct_handle_authorized (GdmSessionDirect *session, +- DBusConnection *connection, ++ GdmSessionConversation *conversation, + DBusMessage *message) + { + DBusMessage *reply; +@@ -401,17 +420,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; +@@ -426,19 +445,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), NULL); ++ _gdm_session_authorization_failed (GDM_SESSION (session), conversation->service_name, NULL); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + static DBusHandlerResult + gdm_session_direct_handle_accredited (GdmSessionDirect *session, +- DBusConnection *connection, ++ GdmSessionConversation *conversation, + DBusMessage *message) + { + DBusMessage *reply; +@@ -446,17 +465,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; +@@ -471,12 +490,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), NULL); ++ _gdm_session_accreditation_failed (GDM_SESSION (session), conversation->service_name, text); + + return DBUS_HANDLER_RESULT_HANDLED; + } +@@ -724,7 +743,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; +@@ -739,7 +758,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'", +@@ -769,7 +788,7 @@ cancel_pending_query (GdmSessionConversation *conversation) + reply = dbus_message_new_error (conversation->message_pending_reply, + GDM_SESSION_DBUS_ERROR_CANCEL, + "Operation cancelled"); +- conversation = session->priv->conversation; ++ conversation = NULL; + dbus_connection_send (conversation->worker_connection, reply, NULL); + dbus_connection_flush (conversation->worker_connection); + +@@ -780,6 +799,7 @@ cancel_pending_query (GdmSessionConversation *conversation) + + static void + answer_pending_query (GdmSessionDirect *session, ++ const char *service_name, + const char *answer) + { + DBusMessage *reply; +@@ -792,7 +812,7 @@ answer_pending_query (GdmSessionDirect *session, + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &answer); + +- conversation = session->priv->conversation; ++ conversation = find_conversation_by_name (session, service_name); + dbus_connection_send (conversation->worker_connection, reply, NULL); + dbus_message_unref (reply); + +@@ -811,7 +831,7 @@ set_pending_query (GdmSessionConversation *conversation, + + static DBusHandlerResult + gdm_session_direct_handle_info_query (GdmSessionDirect *session, +- DBusConnection *connection, ++ GdmSessionConversation *conversation, + DBusMessage *message) + { + DBusError error; +@@ -827,14 +847,14 @@ 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; +@@ -850,14 +870,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; +@@ -872,18 +892,18 @@ 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; +@@ -892,7 +912,7 @@ gdm_session_direct_handle_cancel_pending_query (GdmSessionDirect *session, + cancel_pending_query (conversation); + + reply = dbus_message_new_method_return (message); +- dbus_connection_send (connection, reply, NULL); ++ dbus_connection_send (conversation->worker_connection, reply, NULL); + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +@@ -900,7 +920,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; +@@ -915,18 +935,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_started (GdmSessionDirect *session, +- DBusConnection *connection, ++ GdmSessionConversation *conversation, + DBusMessage *message) + { + DBusMessage *reply; +@@ -945,7 +965,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'", +@@ -954,14 +974,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; +@@ -976,18 +996,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; +@@ -1002,7 +1022,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'", +@@ -1016,7 +1036,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; +@@ -1031,7 +1051,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'", +@@ -1045,7 +1065,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; +@@ -1060,7 +1080,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, +@@ -1078,7 +1098,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; +@@ -1093,7 +1113,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, +@@ -1111,7 +1131,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; +@@ -1126,7 +1146,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)) { +@@ -1154,66 +1174,69 @@ 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, "Authenticated")) { +- return gdm_session_direct_handle_authenticated (session, connection, message); ++ return gdm_session_direct_handle_authenticated (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AuthenticationFailed")) { +- return gdm_session_direct_handle_authentication_failed (session, connection, message); ++ return gdm_session_direct_handle_authentication_failed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Authorized")) { +- return gdm_session_direct_handle_authorized (session, connection, message); ++ return gdm_session_direct_handle_authorized (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AuthorizationFailed")) { +- return gdm_session_direct_handle_authorization_failed (session, connection, message); ++ return gdm_session_direct_handle_authorization_failed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Accredited")) { +- return gdm_session_direct_handle_accredited (session, connection, message); ++ return gdm_session_direct_handle_accredited (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AccreditationFailed")) { +- return gdm_session_direct_handle_accreditation_failed (session, connection, message); ++ return gdm_session_direct_handle_accreditation_failed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "UsernameChanged")) { +- return gdm_session_direct_handle_username_changed (session, connection, message); ++ return gdm_session_direct_handle_username_changed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionStarted")) { +- return gdm_session_direct_handle_session_started (session, connection, message); ++ return gdm_session_direct_handle_session_started (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "StartFailed")) { +- return gdm_session_direct_handle_start_failed (session, connection, message); ++ return gdm_session_direct_handle_start_failed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionExited")) { +- return gdm_session_direct_handle_session_exited (session, connection, message); ++ return gdm_session_direct_handle_session_exited (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionDied")) { +- return gdm_session_direct_handle_session_died (session, connection, message); ++ return gdm_session_direct_handle_session_died (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SavedLanguageNameRead")) { +- return gdm_session_direct_handle_saved_language_name_read (session, connection, message); ++ return gdm_session_direct_handle_saved_language_name_read (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SavedLayoutNameRead")) { +- return gdm_session_direct_handle_saved_layout_name_read (session, connection, message); ++ return gdm_session_direct_handle_saved_layout_name_read (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SavedSessionNameRead")) { +- return gdm_session_direct_handle_saved_session_name_read (session, connection, message); ++ return gdm_session_direct_handle_saved_session_name_read (session, conversation, message); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +@@ -1447,6 +1470,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, +@@ -1461,40 +1505,83 @@ 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 +@@ -1503,50 +1590,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 +@@ -1658,7 +1718,7 @@ worker_exited (GdmSessionWorkerJob *job, + char *msg; + + msg = g_strdup_printf (_("worker exited with status %d"), code); +- _gdm_session_authentication_failed (GDM_SESSION (conversation->session), msg); ++ _gdm_session_authentication_failed (GDM_SESSION (conversation->session), conversation->service_name, msg); + g_free (msg); + } else if (conversation->session->priv->is_running) { + _gdm_session_session_exited (GDM_SESSION (conversation->session), code); +@@ -1676,7 +1736,7 @@ worker_died (GdmSessionWorkerJob *job, + char *msg; + + msg = g_strdup_printf (_("worker exited with status %d"), signum); +- _gdm_session_authentication_failed (GDM_SESSION (conversation->session), msg); ++ _gdm_session_authentication_failed (GDM_SESSION (conversation->session), conversation->service_name, msg); + g_free (msg); + } else if (conversation->session->priv->is_running) { + _gdm_session_session_died (GDM_SESSION (conversation->session), signum); +@@ -1770,10 +1830,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 +@@ -1924,45 +1980,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"); +@@ -1976,13 +2037,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, +@@ -1992,7 +2052,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"); + } +@@ -2001,12 +2060,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 * +@@ -2120,7 +2180,8 @@ setup_session_environment (GdmSessionDirect *session) + } + + 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; +@@ -2134,10 +2195,11 @@ gdm_session_direct_start_session (GdmSession *session) + program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command); + g_free (command); + ++ conversation = find_conversation_by_name (impl, service_name); ++ + setup_session_environment (impl); +- send_environment (impl); ++ send_environment (impl, conversation); + +- conversation = impl->priv->conversation; + send_dbus_string_signal (conversation, "StartProgram", program); + g_free (program); + } +@@ -2183,6 +2245,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; + +@@ -2215,13 +2282,14 @@ 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); + + g_return_if_fail (session != NULL); + +- answer_pending_query (impl, text); ++ answer_pending_query (impl, service_name, text); + } + + static void +@@ -2245,7 +2313,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); + +@@ -2255,9 +2324,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 +@@ -2265,7 +2340,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); + +@@ -2275,9 +2351,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 +@@ -2285,7 +2367,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); + +@@ -2295,9 +2378,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 074aa6f..de6e54a 100644 +--- a/daemon/gdm-session-private.h ++++ b/daemon/gdm-session-private.h +@@ -29,24 +29,34 @@ 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_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); +@@ -67,12 +77,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 35b4738..6e15f75 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 (); +@@ -245,10 +248,11 @@ gdm_session_relay_accredit (GdmSession *session, + + 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 +@@ -292,11 +296,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 +@@ -334,10 +339,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); +@@ -349,7 +356,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; + } +@@ -361,12 +368,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); +@@ -378,7 +387,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; + } +@@ -390,12 +399,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); +@@ -407,7 +418,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; + } +@@ -419,12 +430,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); +@@ -436,7 +449,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; + } +@@ -448,8 +461,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"); + +@@ -457,7 +477,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; + } +@@ -469,8 +489,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"); + +@@ -478,7 +505,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; + } +@@ -491,8 +518,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"); + +@@ -500,7 +534,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; + } +@@ -512,8 +546,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"); + +@@ -521,7 +562,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; + } +@@ -533,8 +574,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"); + +@@ -542,7 +590,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; + } +@@ -554,8 +602,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"); + +@@ -563,7 +618,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; + } +@@ -575,8 +630,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"); + +@@ -584,7 +646,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; + } +@@ -596,8 +658,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"); + +@@ -605,7 +674,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; + } +@@ -617,6 +686,7 @@ handle_session_started (GdmSessionRelay *session_relay, + { + DBusMessage *reply; + DBusError error; ++ char *service_name; + int pid; + + dbus_error_init (&error); +@@ -624,6 +694,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); +@@ -636,6 +707,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 ea4b8bd..b986189 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -2564,6 +2564,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, +@@ -2590,6 +2612,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 0073294..5f6ff5d 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -24,6 +24,7 @@ + #include + #include + ++#include "gdm-marshal.h" + #include "gdm-session.h" + #include "gdm-session-private.h" + +@@ -114,37 +115,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 +@@ -192,11 +197,12 @@ gdm_session_cancel (GdmSession *session) + } + + void +-gdm_session_start_session (GdmSession *session) ++gdm_session_start_session (GdmSession *session, ++ const char *service_name) + { + g_return_if_fail (GDM_IS_SESSION (session)); + +- GDM_SESSION_GET_IFACE (session)->start_session (session); ++ GDM_SESSION_GET_IFACE (session)->start_session (session, service_name); + } + + static void +@@ -213,7 +219,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, +@@ -221,9 +227,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, +@@ -231,10 +238,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, +@@ -263,9 +270,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, +@@ -273,10 +280,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, +@@ -284,9 +291,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, +@@ -294,10 +301,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, +@@ -305,9 +312,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, +@@ -315,10 +322,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", +@@ -327,10 +334,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, +@@ -338,10 +345,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, +@@ -349,10 +356,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, +@@ -360,10 +367,10 @@ gdm_session_class_init (gpointer g_iface) + G_STRUCT_OFFSET (GdmSessionIface, problem), + NULL, + NULL, +- g_cclosure_marshal_VOID__STRING, ++ gdm_marshal_VOID__STRING_STRING, + G_TYPE_NONE, +- 1, +- G_TYPE_STRING); ++ 2, ++ G_TYPE_STRING, G_TYPE_STRING); + signals [SESSION_STARTED] = + g_signal_new ("session-started", + iface_type, +@@ -371,10 +378,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, +@@ -382,10 +389,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, +@@ -465,19 +472,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 +@@ -497,99 +506,111 @@ _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_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 77e0cf6..db5fc4a 100644 +--- a/daemon/gdm-session.h ++++ b/daemon/gdm-session.h +@@ -53,11 +53,15 @@ 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 (* answer_query) (GdmSession *session, ++ const char *service_name, + const char *text); + void (* select_language) (GdmSession *session, + const char *text); +@@ -67,38 +71,53 @@ struct _GdmSessionIface + const char *text); + void (* select_user) (GdmSession *session, + const char *text); +- void (* start_session) (GdmSession *session); ++ void (* start_session) (GdmSession *session, ++ const char *service_name); + void (* close) (GdmSession *session); + void (* cancel) (GdmSession *session); + + /* Signals */ +- void (* setup_complete) (GdmSession *session); ++ void (* setup_complete) (GdmSession *session, ++ const char *service_name); + void (* setup_failed) (GdmSession *session, ++ const char *service_name, + const char *message); + void (* reset_complete) (GdmSession *session); + void (* reset_failed) (GdmSession *session, + const char *message); +- void (* authenticated) (GdmSession *session); ++ void (* authenticated) (GdmSession *session, ++ const char *service_name); + void (* authentication_failed) (GdmSession *session, ++ const char *service_name, + const char *message); +- void (* authorized) (GdmSession *session); ++ void (* authorized) (GdmSession *session, ++ const char *service_name); + void (* authorization_failed) (GdmSession *session, ++ const char *service_name, + const char *message); +- void (* accredited) (GdmSession *session); ++ void (* accredited) (GdmSession *session, ++ const char *service_name); + void (* accreditation_failed) (GdmSession *session, ++ const char *service_name, + const char *message); + + void (* info_query) (GdmSession *session, ++ const char *service_name, + const char *query_text); + void (* secret_info_query) (GdmSession *session, ++ const char *service_name, + const char *query_text); + void (* info) (GdmSession *session, ++ const char *service_name, + const char *info); + void (* problem) (GdmSession *session, ++ const char *service_name, + const char *problem); + void (* session_started) (GdmSession *session, ++ const char *service_name, + int pid); + void (* session_start_failed) (GdmSession *session, ++ const char *service_name, + const char *message); + void (* session_exited) (GdmSession *session, + int exit_code); +@@ -128,14 +147,19 @@ void gdm_session_setup_for_user (GdmSession *session, + const char *service_name, + const char *username); + void gdm_session_reset (GdmSession *session); +-void gdm_session_authenticate (GdmSession *session); +-void gdm_session_authorize (GdmSession *session); ++void gdm_session_authenticate (GdmSession *session, ++ const char *service_name); ++void gdm_session_authorize (GdmSession *session, ++ const char *service_name); + void gdm_session_accredit (GdmSession *session, ++ const char *service_name, + int cred_flag); +-void gdm_session_start_session (GdmSession *session); ++void gdm_session_start_session (GdmSession *session, ++ const char *service_name); + void gdm_session_close (GdmSession *session); + + void gdm_session_answer_query (GdmSession *session, ++ const char *service_name, + const char *text); + void gdm_session_select_session (GdmSession *session, + const char *session_name); +diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c +index 30faaa6..e18f907 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) + { +@@ -204,18 +207,21 @@ 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, ++ service_name, + _("Unable to initialize login system")); + } + +@@ -240,18 +246,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, + _("Unable to authenticate user")); + } + destroy_session (slave); +@@ -259,7 +268,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; +@@ -280,7 +290,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; + } +@@ -288,24 +298,27 @@ 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, ++ service_name, + _("Unable to authorize user")); + } + +@@ -386,31 +399,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) + { +- queue_start_session (slave); ++ queue_start_session (slave, service_name); + } + + static void + on_session_accreditation_failed (GdmSession *session, ++ const char *service_name, + const char *message, + GdmSimpleSlave *slave) + { +@@ -425,6 +445,7 @@ on_session_accreditation_failed (GdmSession *session, + if (! migrated) { + if (slave->priv->greeter_server != NULL) { + gdm_greeter_server_problem (slave->priv->greeter_server, ++ service_name, + _("Unable establish credentials")); + } + } +@@ -440,41 +461,45 @@ on_session_accreditation_failed (GdmSession *session, + + 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 +@@ -744,11 +769,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 +@@ -764,21 +790,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 +@@ -841,18 +869,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 d9fa26e..3cf8483 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 1e2c55d..0bd27a9 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 40d5e1f..ca6fb4f 100644 +--- a/gui/simple-greeter/gdm-greeter-session.c ++++ b/gui/simple-greeter/gdm-greeter-session.c +@@ -64,6 +64,7 @@ static gpointer session_object = NULL; + + static void + on_info (GdmGreeterClient *client, ++ const char *service_name, + const char *text, + GdmGreeterSession *session) + { +@@ -74,6 +75,7 @@ on_info (GdmGreeterClient *client, + + static void + on_problem (GdmGreeterClient *client, ++ const char *service_name, + const char *text, + GdmGreeterSession *session) + { +@@ -160,6 +162,7 @@ on_user_authorized (GdmGreeterClient *client, + + static void + on_info_query (GdmGreeterClient *client, ++ const char *service_name, + const char *text, + GdmGreeterSession *session) + { +@@ -170,6 +173,7 @@ on_info_query (GdmGreeterClient *client, + + static void + on_secret_info_query (GdmGreeterClient *client, ++ const char *service_name, + const char *text, + GdmGreeterSession *session) + { +@@ -191,7 +195,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 +@@ -200,6 +205,7 @@ on_begin_verification_for_user (GdmGreeterLoginWindow *login_window, + GdmGreeterSession *session) + { + gdm_greeter_client_call_begin_verification_for_user (session->priv->client, ++ "gdm", + username); + } + +@@ -209,6 +215,7 @@ on_query_answer (GdmGreeterLoginWindow *login_window, + GdmGreeterSession *session) + { + gdm_greeter_client_call_answer_query (session->priv->client, ++ "gdm", + text); + } + +@@ -266,7 +273,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.6.2.2 + + +From e1627378385cacea168afeb61771dc1c1536db70 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sat, 7 Feb 2009 11:36:40 -0500 +Subject: [PATCH 11/33] 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, + " \n" + " \n" + " \n" ++ " \n" ++ " \n" ++ " \n" + " \n" + " \n" + " \n" +@@ -868,6 +879,9 @@ do_introspect (DBusConnection *connection, + " \n" + " \n" + " \n" ++ " \n" ++ " \n" ++ " \n" + " \n" + " \n" + " \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 0f7e44e..e5296a4 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -1714,6 +1714,7 @@ worker_exited (GdmSessionWorkerJob *job, + { + g_debug ("GdmSessionDirect: Worker job exited: %d", code); + ++ g_object_ref (conversation); + if (!conversation->session->priv->is_authenticated) { + char *msg; + +@@ -1723,6 +1724,11 @@ worker_exited (GdmSessionWorkerJob *job, + } else if (conversation->session->priv->is_running) { + _gdm_session_session_exited (GDM_SESSION (conversation->session), code); + } ++ ++ g_debug ("GdmSessionDirect: Emitting conversation-stopped signal"); ++ _gdm_session_conversation_stopped (GDM_SESSION (conversation->session), ++ conversation->service_name); ++ g_object_unref (conversation); + } + + static void +@@ -1732,6 +1738,7 @@ worker_died (GdmSessionWorkerJob *job, + { + g_debug ("GdmSessionDirect: Worker job died: %d", signum); + ++ g_object_ref (conversation); + if (!conversation->session->priv->is_authenticated) { + char *msg; + +@@ -1741,6 +1748,11 @@ worker_died (GdmSessionWorkerJob *job, + } else if (conversation->session->priv->is_running) { + _gdm_session_session_died (GDM_SESSION (conversation->session), signum); + } ++ ++ g_debug ("GdmSessionDirect: Emitting conversation-stopped signal"); ++ _gdm_session_conversation_stopped (GDM_SESSION (conversation->session), ++ conversation->service_name); ++ g_object_unref (conversation); + } + + static GdmSessionConversation * +@@ -1813,6 +1825,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 +@@ -1833,6 +1849,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) + { +@@ -2646,6 +2681,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 de6e54a..860c09c 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 5f6ff5d..9ee34af 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, +@@ -88,6 +89,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)); +@@ -220,6 +230,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, +@@ -638,6 +658,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 db5fc4a..c45a770 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, +@@ -125,6 +127,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); +@@ -141,6 +145,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 e18f907..d08613f 100644 +--- a/daemon/gdm-simple-slave.c ++++ b/daemon/gdm-simple-slave.c +@@ -225,8 +225,7 @@ on_session_setup_failed (GdmSession *session, + _("Unable to initialize login system")); + } + +- destroy_session (slave); +- queue_greeter_reset (slave); ++ gdm_session_stop_conversation (session, service_name); + } + + static void +@@ -263,8 +262,8 @@ on_session_authentication_failed (GdmSession *session, + service_name, + _("Unable to authenticate user")); + } +- destroy_session (slave); +- queue_greeter_reset (slave); ++ ++ gdm_session_stop_conversation (session, service_name); + } + + static void +@@ -322,8 +321,7 @@ on_session_authorization_failed (GdmSession *session, + _("Unable to authorize user")); + } + +- destroy_session (slave); +- queue_greeter_reset (slave); ++ gdm_session_stop_conversation (session, service_name); + } + + static gboolean +@@ -454,9 +452,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 +@@ -542,6 +539,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) +@@ -630,6 +644,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.6.2.2 + + +From 0f3e91582818b7cabd9b0b44e06b624759a1e8ae Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 9 Mar 2009 15:41:12 -0400 +Subject: [PATCH 12/33] Don't tear down greeter until pam_open_session finishes + +Some PAM modules ask questions at that late stage of the game, +and so we need a greeter to forward the questions on to the +user. +--- + daemon/gdm-factory-slave.c | 37 ++++++++++++++++++++-- + daemon/gdm-product-slave.c | 30 +++++++++++++++++- + daemon/gdm-session-direct.c | 70 ++++++++++++++++++++++++++++++++++++++++++ + daemon/gdm-session-private.h | 5 +++ + daemon/gdm-session-relay.c | 68 ++++++++++++++++++++++++++++++++++++++++ + daemon/gdm-session-worker.c | 16 ++++++++-- + daemon/gdm-session.c | 50 ++++++++++++++++++++++++++++++ + daemon/gdm-session.h | 9 +++++ + daemon/gdm-simple-slave.c | 33 +++++++++++++++++++- + 9 files changed, 310 insertions(+), 8 deletions(-) + +diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c +index 826612e..ef6d236 100644 +--- a/daemon/gdm-factory-slave.c ++++ b/daemon/gdm-factory-slave.c +@@ -278,9 +278,7 @@ on_session_accredited (GdmSession *session, + { + g_debug ("GdmFactorySlave: session user verified"); + +- gdm_session_start_session (session, service_name); +- +- gdm_greeter_server_reset (slave->priv->greeter_server); ++ gdm_session_open_session (session, service_name); + } + + static void +@@ -298,6 +296,31 @@ on_session_accreditation_failed (GdmSession *session, + } + + static void ++on_session_opened (GdmSession *session, ++ const char *service_name, ++ GdmFactorySlave *slave) ++{ ++ g_debug ("GdmFactorySlave: session opened"); ++ ++ gdm_session_start_session (session, service_name); ++ ++ gdm_greeter_server_reset (slave->priv->greeter_server); ++} ++ ++static void ++on_session_open_failed (GdmSession *session, ++ const char *service_name, ++ const char *message, ++ GdmFactorySlave *slave) ++{ ++ g_debug ("GdmFactorySlave: could not open session: %s", message); ++ ++ gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to open session")); ++ ++ queue_greeter_reset (slave); ++} ++ ++static void + on_session_session_started (GdmSession *session, + GdmFactorySlave *slave) + { +@@ -767,6 +790,14 @@ gdm_factory_slave_start (GdmSlave *slave) + G_CALLBACK (on_session_accreditation_failed), + slave); + g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session, ++ "session-opened", ++ G_CALLBACK (on_session_opened), ++ slave); ++ g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session, ++ "session-open-failed", ++ G_CALLBACK (on_session_open_failed), ++ slave); ++ g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session, + "info", + G_CALLBACK (on_session_info), + slave); +diff --git a/daemon/gdm-product-slave.c b/daemon/gdm-product-slave.c +index dd2e1bc..93d83a1 100644 +--- a/daemon/gdm-product-slave.c ++++ b/daemon/gdm-product-slave.c +@@ -635,6 +635,27 @@ on_session_accreditation_failed (GdmSession *session, + } + + static void ++on_session_opened (GdmSession *session, ++ const char *service_name, ++ GdmProductSlave *slave) ++{ ++ send_dbus_string_method (slave->priv->session_relay_connection, ++ "SessionOpened", service_name); ++} ++ ++static void ++on_session_open_failed (GdmSession *session, ++ const char *service_name, ++ const char *message, ++ GdmProductSlave *slave) ++{ ++ send_dbus_string_string_method (slave->priv->session_relay_connection, ++ "SessionOpenFailed", ++ service_name, ++ message); ++} ++ ++static void + on_session_info (GdmSession *session, + const char *service_name, + const char *text, +@@ -1051,7 +1072,14 @@ create_new_session (GdmProductSlave *slave) + "accreditation-failed", + G_CALLBACK (on_session_accreditation_failed), + slave); +- ++ g_signal_connect (slave->priv->session, ++ "session-opened", ++ G_CALLBACK (on_session_opened), ++ slave); ++ g_signal_connect (slave->priv->session, ++ "session-open-failed", ++ G_CALLBACK (on_session_open_failed), ++ slave); + g_signal_connect (slave->priv->session, + "info", + G_CALLBACK (on_session_info), +diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c +index e5296a4..805adbf 100644 +--- a/daemon/gdm-session-direct.c ++++ b/daemon/gdm-session-direct.c +@@ -945,6 +945,58 @@ gdm_session_direct_handle_problem (GdmSessionDirect *session, + } + + static DBusHandlerResult ++gdm_session_direct_handle_session_opened (GdmSessionDirect *session, ++ GdmSessionConversation *conversation, ++ DBusMessage *message) ++{ ++ DBusMessage *reply; ++ DBusError error; ++ ++ g_debug ("GdmSessionDirect: Handling SessionOpened"); ++ ++ dbus_error_init (&error); ++ if (! dbus_message_get_args (message, &error, DBUS_TYPE_INVALID)) { ++ g_warning ("ERROR: %s", error.message); ++ } ++ ++ g_debug ("GdmSessionDirect: Emitting 'session-opened' signal"); ++ ++ _gdm_session_session_opened (GDM_SESSION (session), conversation->service_name); ++ ++ reply = dbus_message_new_method_return (message); ++ dbus_connection_send (conversation->worker_connection, reply, NULL); ++ dbus_message_unref (reply); ++ ++ return DBUS_HANDLER_RESULT_HANDLED; ++} ++ ++static DBusHandlerResult ++gdm_session_direct_handle_open_failed (GdmSessionDirect *session, ++ GdmSessionConversation *conversation, ++ DBusMessage *message) ++{ ++ DBusMessage *reply; ++ DBusError error; ++ const char *text; ++ ++ dbus_error_init (&error); ++ if (! dbus_message_get_args (message, &error, ++ DBUS_TYPE_STRING, &text, ++ DBUS_TYPE_INVALID)) { ++ g_warning ("ERROR: %s", error.message); ++ } ++ ++ reply = dbus_message_new_method_return (message); ++ dbus_connection_send (conversation->worker_connection, reply, NULL); ++ dbus_message_unref (reply); ++ ++ g_debug ("GdmSessionDirect: Emitting 'session-open-failed' signal"); ++ _gdm_session_session_open_failed (GDM_SESSION (session), conversation->service_name, text); ++ ++ return DBUS_HANDLER_RESULT_HANDLED; ++} ++ ++static DBusHandlerResult + gdm_session_direct_handle_session_started (GdmSessionDirect *session, + GdmSessionConversation *conversation, + DBusMessage *message) +@@ -1223,6 +1275,10 @@ session_worker_message (DBusConnection *connection, + return gdm_session_direct_handle_accreditation_failed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "UsernameChanged")) { + return gdm_session_direct_handle_username_changed (session, conversation, message); ++ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionOpened")) { ++ return gdm_session_direct_handle_session_opened (session, conversation, message); ++ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "OpenFailed")) { ++ return gdm_session_direct_handle_open_failed (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionStarted")) { + return gdm_session_direct_handle_session_started (session, conversation, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "StartFailed")) { +@@ -2215,6 +2271,19 @@ setup_session_environment (GdmSessionDirect *session) + } + + static void ++gdm_session_direct_open_session (GdmSession *session, ++ const char *service_name) ++{ ++ GdmSessionDirect *impl = GDM_SESSION_DIRECT (session); ++ GdmSessionConversation *conversation; ++ ++ g_return_if_fail (session != NULL); ++ ++ conversation = find_conversation_by_name (impl, service_name); ++ send_dbus_void_signal (conversation, "OpenSession"); ++} ++ ++static void + gdm_session_direct_start_session (GdmSession *session, + const char *service_name) + { +@@ -2687,6 +2756,7 @@ gdm_session_iface_init (GdmSessionIface *iface) + iface->authenticate = gdm_session_direct_authenticate; + iface->authorize = gdm_session_direct_authorize; + iface->accredit = gdm_session_direct_accredit; ++ iface->open_session = gdm_session_direct_open_session; + iface->close = gdm_session_direct_close; + + iface->cancel = gdm_session_direct_cancel; +diff --git a/daemon/gdm-session-private.h b/daemon/gdm-session-private.h +index 860c09c..36781dd 100644 +--- a/daemon/gdm-session-private.h ++++ b/daemon/gdm-session-private.h +@@ -54,6 +54,11 @@ void _gdm_session_accredited (GdmSession *sessio + void _gdm_session_accreditation_failed (GdmSession *session, + const char *service_name, + const char *text); ++void _gdm_session_session_opened (GdmSession *session, ++ const char *service_name); ++void _gdm_session_session_open_failed (GdmSession *session, ++ const char *service_name, ++ const char *message); + void _gdm_session_session_started (GdmSession *session, + const char *service_name, + int pid); +diff --git a/daemon/gdm-session-relay.c b/daemon/gdm-session-relay.c +index 6e15f75..3bf8ed7 100644 +--- a/daemon/gdm-session-relay.c ++++ b/daemon/gdm-session-relay.c +@@ -247,6 +247,14 @@ gdm_session_relay_accredit (GdmSession *session, + } + + static void ++gdm_session_relay_open_session (GdmSession *session, ++ const char *service_name) ++{ ++ GdmSessionRelay *impl = GDM_SESSION_RELAY (session); ++ send_dbus_string_signal (impl, "OpenSession", service_name); ++} ++ ++static void + gdm_session_relay_answer_query (GdmSession *session, + const char *service_name, + const char *text) +@@ -678,6 +686,61 @@ handle_accreditation_failed (GdmSessionRelay *session_relay, + + return DBUS_HANDLER_RESULT_HANDLED; + } ++static DBusHandlerResult ++handle_session_opened (GdmSessionRelay *session_relay, ++ DBusConnection *connection, ++ DBusMessage *message) ++{ ++ DBusMessage *reply; ++ DBusError error; ++ char *service_name; ++ ++ dbus_error_init (&error); ++ if (! dbus_message_get_args (message, &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID)) { ++ g_warning ("ERROR: %s", error.message); ++ } ++ dbus_error_free (&error); ++ ++ g_debug ("GdmSessionRelay: Session Opened"); ++ ++ reply = dbus_message_new_method_return (message); ++ dbus_connection_send (connection, reply, NULL); ++ dbus_message_unref (reply); ++ ++ _gdm_session_session_opened (GDM_SESSION (session_relay), service_name); ++ ++ return DBUS_HANDLER_RESULT_HANDLED; ++} ++ ++static DBusHandlerResult ++handle_session_open_failed (GdmSessionRelay *session_relay, ++ DBusConnection *connection, ++ DBusMessage *message) ++{ ++ DBusMessage *reply; ++ DBusError error; ++ char *service_name; ++ ++ dbus_error_init (&error); ++ if (! dbus_message_get_args (message, &error, ++ DBUS_TYPE_STRING, &service_name, ++ DBUS_TYPE_INVALID)) { ++ g_warning ("ERROR: %s", error.message); ++ } ++ dbus_error_free (&error); ++ ++ g_debug ("GdmSessionRelay: Session Open Failed"); ++ ++ reply = dbus_message_new_method_return (message); ++ dbus_connection_send (connection, reply, NULL); ++ dbus_message_unref (reply); ++ ++ _gdm_session_session_open_failed (GDM_SESSION (session_relay), service_name, NULL); ++ ++ return DBUS_HANDLER_RESULT_HANDLED; ++} + + static DBusHandlerResult + handle_session_started (GdmSessionRelay *session_relay, +@@ -794,6 +857,10 @@ session_handle_child_message (DBusConnection *connection, + return handle_accredited (session_relay, connection, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "AccreditationFailed")) { + return handle_accreditation_failed (session_relay, connection, message); ++ } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionOpened")) { ++ return handle_session_opened (session_relay, connection, message); ++ } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionOpenFailed")) { ++ return handle_session_open_failed (session_relay, connection, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionStarted")) { + return handle_session_started (session_relay, connection, message); + } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionStopped")) { +@@ -1193,6 +1260,7 @@ gdm_session_iface_init (GdmSessionIface *iface) + iface->authenticate = gdm_session_relay_authenticate; + iface->authorize = gdm_session_relay_authorize; + iface->accredit = gdm_session_relay_accredit; ++ iface->open_session = gdm_session_relay_open_session; + iface->close = gdm_session_relay_close; + + iface->cancel = gdm_session_relay_cancel; +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index b986189..302d608 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -2187,13 +2187,13 @@ do_open_session (GdmSessionWorker *worker) + res = gdm_session_worker_open_user_session (worker, &error); + if (! res) { + send_dbus_string_method (worker->priv->connection, +- "StartFailed", ++ "OpenFailed", + error->message); + g_error_free (error); + return; + } + +- queue_state_change (worker); ++ send_dbus_void_method (worker->priv->connection, "SessionOpened"); + } + + static void +@@ -2309,7 +2309,7 @@ on_start_program (GdmSessionWorker *worker, + const char *text; + dbus_bool_t res; + +- /* FIXME: return error if not in ACCREDITED state */ ++ /* FIXME: return error if not in SESSION_OPENED state */ + + dbus_error_init (&error); + res = dbus_message_get_args (message, +@@ -2448,6 +2448,14 @@ on_establish_credentials (GdmSessionWorker *worker, + } + + static void ++on_open_session (GdmSessionWorker *worker, ++ DBusMessage *message) ++{ ++ /* FIXME: return error if not in ACCREDITED state */ ++ queue_state_change (worker); ++} ++ ++static void + on_reauthenticate (GdmSessionWorker *worker, + DBusMessage *message) + { +@@ -2507,6 +2515,8 @@ worker_dbus_handle_message (DBusConnection *connection, + on_authorize (worker, message); + } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "EstablishCredentials")) { + on_establish_credentials (worker, message); ++ } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "OpenSession")) { ++ on_open_session (worker, message); + } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "StartProgram")) { + on_start_program (worker, message); + } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Reauthenticate")) { +diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c +index 9ee34af..8858071 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -46,6 +46,8 @@ enum { + PROBLEM, + INFO_QUERY, + SECRET_INFO_QUERY, ++ SESSION_OPENED, ++ SESSION_OPEN_FAILED, + SESSION_STARTED, + SESSION_START_FAILED, + SESSION_EXITED, +@@ -207,6 +209,15 @@ gdm_session_cancel (GdmSession *session) + } + + void ++gdm_session_open_session (GdmSession *session, ++ const char *service_name) ++{ ++ g_return_if_fail (GDM_IS_SESSION (session)); ++ ++ GDM_SESSION_GET_IFACE (session)->open_session (session, service_name); ++} ++ ++void + gdm_session_start_session (GdmSession *session, + const char *service_name) + { +@@ -391,6 +402,28 @@ gdm_session_class_init (gpointer g_iface) + G_TYPE_NONE, + 2, + G_TYPE_STRING, G_TYPE_STRING); ++ signals [SESSION_OPENED] = ++ g_signal_new ("session-opened", ++ iface_type, ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (GdmSessionIface, session_opened), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__STRING, ++ G_TYPE_NONE, ++ 1, ++ G_TYPE_STRING); ++ signals [SESSION_OPEN_FAILED] = ++ g_signal_new ("session-open-failed", ++ iface_type, ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (GdmSessionIface, session_open_failed), ++ NULL, ++ NULL, ++ gdm_marshal_VOID__STRING_STRING, ++ G_TYPE_NONE, ++ 2, ++ G_TYPE_STRING, G_TYPE_STRING); + signals [SESSION_STARTED] = + g_signal_new ("session-started", + iface_type, +@@ -616,6 +649,23 @@ _gdm_session_problem (GdmSession *session, + } + + void ++_gdm_session_session_opened (GdmSession *session, ++ const char *service_name) ++{ ++ g_return_if_fail (GDM_IS_SESSION (session)); ++ g_signal_emit (session, signals [SESSION_OPENED], 0, service_name); ++} ++ ++void ++_gdm_session_session_open_failed (GdmSession *session, ++ const char *service_name, ++ const char *text) ++{ ++ g_return_if_fail (GDM_IS_SESSION (session)); ++ g_signal_emit (session, signals [SESSION_OPEN_FAILED], 0, service_name, text); ++} ++ ++void + _gdm_session_session_started (GdmSession *session, + const char *service_name, + int pid) +diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h +index c45a770..22c2ccb 100644 +--- a/daemon/gdm-session.h ++++ b/daemon/gdm-session.h +@@ -62,6 +62,8 @@ struct _GdmSessionIface + void (* accredit) (GdmSession *session, + const char *service_name, + int cred_flag); ++ void (* open_session) (GdmSession *session, ++ const char *service_name); + void (* answer_query) (GdmSession *session, + const char *service_name, + const char *text); +@@ -115,6 +117,11 @@ struct _GdmSessionIface + void (* problem) (GdmSession *session, + const char *service_name, + const char *problem); ++ void (* session_opened) (GdmSession *session, ++ const char *service_name); ++ void (* session_open_failed) (GdmSession *session, ++ const char *service_name, ++ const char *message); + void (* session_started) (GdmSession *session, + const char *service_name, + int pid); +@@ -160,6 +167,8 @@ void gdm_session_authorize (GdmSession *session, + void gdm_session_accredit (GdmSession *session, + const char *service_name, + int cred_flag); ++void gdm_session_open_session (GdmSession *session, ++ const char *service_name); + void gdm_session_start_session (GdmSession *session, + const char *service_name); + void gdm_session_close (GdmSession *session); +diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c +index d08613f..17f5f7a 100644 +--- a/daemon/gdm-simple-slave.c ++++ b/daemon/gdm-simple-slave.c +@@ -423,7 +423,7 @@ on_session_accredited (GdmSession *session, + const char *service_name, + GdmSimpleSlave *slave) + { +- queue_start_session (slave, service_name); ++ gdm_session_open_session (session, service_name); + } + + static void +@@ -457,6 +457,29 @@ on_session_accreditation_failed (GdmSession *session, + } + + static void ++on_session_opened (GdmSession *session, ++ const char *service_name, ++ GdmSimpleSlave *slave) ++{ ++ queue_start_session (slave, service_name); ++} ++ ++static void ++on_session_open_failed (GdmSession *session, ++ const char *service_name, ++ const char *message, ++ GdmSimpleSlave *slave) ++{ ++ if (slave->priv->greeter_server != NULL) { ++ gdm_greeter_server_problem (slave->priv->greeter_server, ++ service_name, ++ _("Unable to open session")); ++ } ++ ++ gdm_session_stop_conversation (session, service_name); ++} ++ ++static void + on_session_info (GdmSession *session, + const char *service_name, + const char *text, +@@ -688,6 +711,14 @@ create_new_session (GdmSimpleSlave *slave) + G_CALLBACK (on_session_accreditation_failed), + slave); + g_signal_connect (slave->priv->session, ++ "session-opened", ++ G_CALLBACK (on_session_opened), ++ slave); ++ g_signal_connect (slave->priv->session, ++ "session-open-failed", ++ G_CALLBACK (on_session_open_failed), ++ slave); ++ g_signal_connect (slave->priv->session, + "info", + G_CALLBACK (on_session_info), + slave); +-- +1.6.2.2 + + +From 95ca599fa5fe182bc09ab4ff359cdb8e67a17410 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 30 Jan 2009 23:57:31 -0500 +Subject: [PATCH 13/33] 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.glade | 39 ++++- + 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 +++++++ + 7 files changed, 409 insertions(+), 28 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 9b5834d..e2d3996 100644 +--- a/gui/simple-greeter/Makefile.am ++++ b/gui/simple-greeter/Makefile.am +@@ -83,6 +83,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 = \ +@@ -316,6 +318,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 01596e8..d08404e 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -60,12 +60,16 @@ + #include + #endif + ++#include "gdm-marshal.h" ++ + #include "gdm-settings-client.h" + #include "gdm-settings-keys.h" + #include "gdm-profile.h" + ++#include "gdm-greeter-client.h" + #include "gdm-greeter-login-window.h" + #include "gdm-user-chooser-widget.h" ++#include "gdm-task-list.h" + + #ifdef HAVE_PAM + #include +@@ -111,6 +115,7 @@ struct GdmGreeterLoginWindowPrivate + { + GladeXML *xml; + GtkWidget *user_chooser; ++ GtkWidget *conversation_list; + GtkWidget *auth_banner_label; + guint display_is_local : 1; + guint is_interactive : 1; +@@ -139,6 +144,7 @@ enum { + }; + + enum { ++ START_CONVERSATION, + BEGIN_AUTO_LOGIN, + BEGIN_VERIFICATION, + BEGIN_VERIFICATION_FOR_USER, +@@ -395,7 +401,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 +@@ -747,26 +753,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; +@@ -842,6 +854,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; +@@ -849,6 +862,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 = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry"); +@@ -871,6 +888,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; +@@ -1343,7 +1361,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) { +@@ -1357,7 +1378,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); +@@ -1501,6 +1525,21 @@ 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 GtkWidget * + custom_widget_constructor (GladeXML *xml, + char *func_name, +@@ -1523,6 +1562,8 @@ custom_widget_constructor (GladeXML *xml, + + if (strcmp (name, "user-chooser") == 0) { + widget = gdm_user_chooser_widget_new (); ++ } else if (strcmp (name, "conversation-list") == 0) { ++ widget = gdm_task_list_new (); + } + + gdm_profile_end (NULL); +@@ -1596,6 +1637,25 @@ load_theme (GdmGreeterLoginWindow *login_window) + + gtk_widget_show (login_window->priv->user_chooser); + ++ login_window->priv->conversation_list = glade_xml_get_widget (login_window->priv->xml, ++ "conversation-list"); ++ g_signal_connect_swapped (GDM_TASK_LIST (login_window->priv->conversation_list), ++ "activated", ++ G_CALLBACK (on_task_activated), ++ login_window); ++ g_signal_connect_swapped (GDM_TASK_LIST (login_window->priv->conversation_list), ++ "deactivated", ++ G_CALLBACK (on_task_deactivated), ++ login_window); ++ 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 = glade_xml_get_widget (login_window->priv->xml, "auth-banner-label"); + /*make_label_small_italic (login_window->priv->auth_banner_label);*/ + +@@ -1763,6 +1823,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), +@@ -1779,9 +1848,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), +@@ -1789,9 +1858,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), +@@ -1799,9 +1868,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.glade b/gui/simple-greeter/gdm-greeter-login-window.glade +index b46dcad..3d9e2ee 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.glade ++++ b/gui/simple-greeter/gdm-greeter-login-window.glade +@@ -654,11 +654,38 @@ + 10 + + +- ++ + True +- 0 +- 0 +- Tue, 18 Nov 2008 21:55:38 GMT ++ False ++ 0 ++ ++ ++ ++ True ++ 0 ++ 0 ++ Tue, 18 Nov 2008 21:55:38 GMT ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ 0 ++ 0 ++ Fri, 30 Jan 2009 16:03:30 GMT ++ ++ ++ 0 ++ False ++ False ++ ++ + + + 0 +@@ -778,10 +805,6 @@ + + + +- +- +- +- + + + +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 ca6fb4f..714daa1 100644 +--- a/gui/simple-greeter/gdm-greeter-session.c ++++ b/gui/simple-greeter/gdm-greeter-session.c +@@ -70,7 +70,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 +@@ -81,7 +81,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 +@@ -168,7 +168,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 +@@ -179,10 +179,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) +@@ -193,29 +201,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); + } + +@@ -366,7 +377,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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "gdm-task-list.h" ++ ++#define GDM_TASK_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_TASK_LIST, GdmTaskListPrivate)) ++ ++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 ++ */ ++ ++#ifndef __GDM_TASK_LIST_H ++#define __GDM_TASK_LIST_H ++ ++#include ++#include ++ ++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.6.2.2 + + +From 6c9b5b6334732c53867d863e80a8cc3210cc1563 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 6 Mar 2009 11:19:40 -0500 +Subject: [PATCH 14/33] 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 302d608..d3d356c 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -2050,8 +2050,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), +@@ -2668,6 +2666,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.6.2.2 + + +From 3358198f6e5780ec02fb3adc3dcd7b47b8dae77c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 5 Feb 2009 15:20:25 -0500 +Subject: [PATCH 15/33] 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 17f5f7a..09a7425 100644 +--- a/daemon/gdm-simple-slave.c ++++ b/daemon/gdm-simple-slave.c +@@ -812,6 +812,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); + } +@@ -822,6 +825,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); + } +@@ -832,6 +838,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); +@@ -844,6 +853,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); +@@ -855,6 +867,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); + } + +@@ -863,6 +878,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); + } + +@@ -871,6 +889,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); + } + +@@ -879,6 +900,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); + } + +@@ -895,7 +919,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 +@@ -905,6 +933,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, +@@ -922,6 +953,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) { +@@ -935,6 +969,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.6.2.2 + + +From 7f82493b40ea7c05586029603ebde42c65aa1bec Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 6 Feb 2009 16:23:48 -0500 +Subject: [PATCH 16/33] 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 | 45 ++ + 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 | 675 ++++++++++++++++---- + gui/simple-greeter/gdm-greeter-login-window.glade | 144 +---- + gui/simple-greeter/gdm-greeter-login-window.h | 21 +- + 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 | 216 +++++-- + 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 | 316 +++++++++ + .../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 ++ + 29 files changed, 2988 insertions(+), 297 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 be6580d..f0842c8 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) +@@ -199,6 +215,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 --------------------------------------------------------------------------- + +@@ -1231,6 +1256,22 @@ fi + AC_SUBST(GDM_XAUTH_DIR) + + dnl --------------------------------------------------------------------------- ++dnl - Directory for simple greeter plugins ++dnl --------------------------------------------------------------------------- ++ ++AC_ARG_WITH(simple-greeter-plugins-dir, ++ AS_HELP_STRING([--with-simple-greeter-plugins-dir=], ++ [simple greeter plugins directory])) ++ ++if ! test -z "$with_simple_greeter_plugins_dir"; then ++ GDM_SIMPLE_GREETER_PLUGINS_DIR=$with_simple_greeter_plugins_dir ++else ++ GDM_SIMPLE_GREETER_PLUGINS_DIR=${libdir}/gdm/simple-greeter/plugins ++fi ++ ++AC_SUBST(GDM_SIMPLE_GREETER_PLUGINS_DIR) ++ ++dnl --------------------------------------------------------------------------- + dnl - Finish + dnl --------------------------------------------------------------------------- + +@@ -1357,6 +1398,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 e2d3996..a9e4d65 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)"\" \ +@@ -17,6 +20,7 @@ AM_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DSBINDIR=\""$(sbindir)"\" \ + -DAT_SPI_REGISTRYD_DIR="\"$(AT_SPI_REGISTRYD_DIR)\"" \ ++ -DGDM_SIMPLE_GREETER_PLUGINS_DIR="\"$(GDM_SIMPLE_GREETER_PLUGINS_DIR)\""\ + $(DISABLE_DEPRECATED_CFLAGS) \ + $(GTK_CFLAGS) \ + $(SIMPLE_GREETER_CFLAGS) \ +@@ -85,10 +89,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) \ +@@ -140,6 +149,7 @@ test_greeter_panel_SOURCES = \ + test_greeter_panel_LDADD = \ + $(top_builddir)/common/libgdmcommon.la \ + $(top_builddir)/gui/simple-greeter/libnotificationarea/libnotificationarea.la \ ++ $(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la \ + $(SIMPLE_GREETER_LIBS) \ + $(GTK_LIBS) \ + $(GCONF_LIBS) \ +@@ -312,10 +322,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 \ +@@ -326,6 +340,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 0bd27a9..a9321aa 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 d08404e..2e9a1a0 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 +- * Copyright (C) 2008 Red Hat, Inc. ++ * Copyright (C) 2008, 2009 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -17,6 +17,9 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * ++ * Written by: William Jon McCann ++ * Ray Strode ++ * + */ + + #include "config.h" +@@ -117,6 +120,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; + GConfClient *client; +@@ -166,6 +170,9 @@ static void restart_timed_login_timeout (GdmGreeterLoginWindow *login_window + static void on_user_unchosen (GdmUserChooserWidget *user_chooser, + GdmGreeterLoginWindow *login_window); + ++static void gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window, ++ const char *service_name); ++ + G_DEFINE_TYPE (GdmGreeterLoginWindow, gdm_greeter_login_window, GTK_TYPE_WINDOW) + + static void +@@ -190,9 +197,6 @@ set_sensitive (GdmGreeterLoginWindow *login_window, + { + GtkWidget *box; + +- box = glade_xml_get_widget (login_window->priv->xml, "auth-input-box"); +- gtk_widget_set_sensitive (box, sensitive); +- + box = glade_xml_get_widget (login_window->priv->xml, "buttonbox"); + gtk_widget_set_sensitive (box, sensitive); + +@@ -202,27 +206,43 @@ set_sensitive (GdmGreeterLoginWindow *login_window, + static void + set_focus (GdmGreeterLoginWindow *login_window) + { +- GtkWidget *entry; +- +- entry = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry"); ++ GdmTask *task; + + gdk_window_focus (GTK_WIDGET (login_window)->window, GDK_CURRENT_TIME); + +- if (GTK_WIDGET_REALIZED (entry) && ! GTK_WIDGET_HAS_FOCUS (entry)) { +- gtk_widget_grab_focus (entry); ++ task = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list)); ++ ++ if (gdm_conversation_focus (GDM_CONVERSATION (task))) { ++ char *name; ++ name = gdm_task_get_name (task); ++ g_debug ("GdmGreeterLoginWindow: focusing task %s", name); ++ g_free (name); + } else if (GTK_WIDGET_REALIZED (login_window->priv->user_chooser) && ! GTK_WIDGET_HAS_FOCUS (login_window->priv->user_chooser)) { + gtk_widget_grab_focus (login_window->priv->user_chooser); + } ++ g_object_unref (task); ++} ++ ++static gboolean ++set_task_conversation_message (GdmTaskList *task_list, ++ GdmTask *task, ++ const char *message) ++{ ++ ++ gdm_conversation_set_message (GDM_CONVERSATION (task), message); ++ return FALSE; + } + + static void + set_message (GdmGreeterLoginWindow *login_window, + const char *text) + { +- GtkWidget *label; ++ g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window)); + +- label = glade_xml_get_widget (login_window->priv->xml, "auth-message-label"); +- gtk_label_set_text (GTK_LABEL (label), text); ++ gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list), ++ (GdmTaskListForeachFunc) ++ set_task_conversation_message, ++ (gpointer) text); + } + + static void +@@ -388,30 +408,76 @@ get_show_restart_buttons (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 = glade_xml_get_widget (login_window->priv->xml, "auth-prompt-entry"); +- text = gtk_entry_get_text (GTK_ENTRY (entry)); +- + _gdm_greeter_login_window_set_interactive (login_window, TRUE); +- g_signal_emit (login_window, signals[QUERY_ANSWER], 0, "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 +@@ -419,6 +485,7 @@ set_log_in_button_mode (GdmGreeterLoginWindow *login_window, + int mode) + { + GtkWidget *button; ++ GdmTask *task; + + button = glade_xml_get_widget (login_window->priv->xml, "log-in-button"); + gtk_widget_grab_default (button); +@@ -431,14 +498,27 @@ 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_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: +@@ -562,7 +642,7 @@ switch_mode (GdmGreeterLoginWindow *login_window, + show_widget (login_window, "disconnect-button", + ! login_window->priv->display_is_local); + +- show_widget (login_window, "auth-input-box", FALSE); ++ show_widget (login_window, "auth-page-box", FALSE); + + add_sensitize_power_buttons_timeout (login_window); + sensitize_widget (login_window, "shutdown-button", FALSE); +@@ -578,6 +658,7 @@ switch_mode (GdmGreeterLoginWindow *login_window, + show_widget (login_window, "restart-button", FALSE); + show_widget (login_window, "suspend-button", FALSE); + show_widget (login_window, "disconnect-button", FALSE); ++ show_widget (login_window, "auth-page-box", TRUE); + default_name = "log-in-button"; + break; + default: +@@ -657,25 +738,40 @@ do_suspend (GdmGreeterLoginWindow *login_window) + g_object_unref (proxy); + } + +-static void +-delete_entry_text (GtkWidget *entry) ++static gboolean ++task_has_service_name (GdmTaskList *task_list, ++ GdmTask *task, ++ const char *service_name) + { +- const char *typed_text; +- char *null_text; ++ 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); + +- /* try to scrub out any secret info */ +- typed_text = gtk_entry_get_text (GTK_ENTRY (entry)); +- null_text = g_strnfill (strlen (typed_text) + 1, '\b'); +- gtk_entry_set_text (GTK_ENTRY (entry), null_text); +- gtk_entry_set_text (GTK_ENTRY (entry), ""); ++ return has_service_name; ++} ++ ++static gboolean ++reset_task (GdmTaskList *task_list, ++ GdmTask *task, ++ GdmGreeterLoginWindow *login_window) ++{ ++ char *name; ++ ++ name = gdm_task_get_name (task); ++ g_debug ("Resetting task '%s'", name); ++ g_free (name); ++ ++ gdm_conversation_reset (GDM_CONVERSATION (task)); ++ return FALSE; + } + + static void + reset_dialog (GdmGreeterLoginWindow *login_window) + { +- GtkWidget *entry; +- GtkWidget *label; +- + g_debug ("GdmGreeterLoginWindow: Resetting dialog"); + set_busy (login_window); + set_sensitive (login_window, FALSE); +@@ -699,16 +795,12 @@ reset_dialog (GdmGreeterLoginWindow *login_window) + login_window->priv->start_session_handler_id = 0; + } + +- entry = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry"); +- +- delete_entry_text (entry); ++ gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list), ++ (GdmTaskListForeachFunc) ++ reset_task, ++ login_window); + +- gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE); + set_message (login_window, ""); +- +- label = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-label"); +- gtk_label_set_text (GTK_LABEL (label), ""); +- + switch_mode (login_window, MODE_SELECTION); + + set_sensitive (login_window, TRUE); +@@ -728,11 +820,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)); +@@ -742,12 +845,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; + } + +@@ -756,12 +910,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; +@@ -772,13 +934,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; +@@ -816,11 +987,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 " +@@ -830,8 +1011,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 +@@ -857,26 +1038,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 = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry"); +- delete_entry_text (entry); +- gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE); +- set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY); ++ task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list), ++ (GdmTaskListForeachFunc) ++ task_has_service_name, ++ (gpointer) service_name); + +- label = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-label"); +- gtk_label_set_text (GTK_LABEL (label), text); ++ if (task != NULL) { ++ gdm_conversation_ask_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)); +@@ -891,20 +1070,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 = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry"); +- delete_entry_text (entry); +- gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE); +- set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY); ++ task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list), ++ (GdmTaskListForeachFunc) ++ task_has_service_name, ++ (gpointer) service_name); + +- label = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-label"); +- gtk_label_set_text (GTK_LABEL (label), text); ++ if (task != NULL) { ++ gdm_conversation_ask_secret (GDM_CONVERSATION (task), ++ text); ++ g_object_unref (task); ++ } + +- show_widget (login_window, "auth-input-box", TRUE); ++ set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY); + set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE); + set_ready (GDM_GREETER_LOGIN_WINDOW (login_window)); + set_focus (GDM_GREETER_LOGIN_WINDOW (login_window)); +@@ -915,13 +1097,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 +@@ -1344,6 +1529,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) +@@ -1361,10 +1586,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) { +@@ -1378,10 +1603,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); +@@ -1527,17 +1752,65 @@ 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 GtkWidget * +@@ -1574,7 +1847,6 @@ custom_widget_constructor (GladeXML *xml, + static void + load_theme (GdmGreeterLoginWindow *login_window) + { +- GtkWidget *entry; + GtkWidget *button; + GtkWidget *box; + GtkWidget *image; +@@ -1649,15 +1921,9 @@ 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 = glade_xml_get_widget (login_window->priv->xml, "auth-banner-label"); + /*make_label_small_italic (login_window->priv->auth_banner_label);*/ ++ login_window->priv->auth_page_box = glade_xml_get_widget (login_window->priv->xml, "auth-page-box"); + + button = glade_xml_get_widget (login_window->priv->xml, "suspend-button"); + g_signal_connect (button, "clicked", G_CALLBACK (suspend_button_clicked), login_window); +@@ -1673,14 +1939,6 @@ load_theme (GdmGreeterLoginWindow *login_window) + button = glade_xml_get_widget (login_window->priv->xml, "shutdown-button"); + g_signal_connect (button, "clicked", G_CALLBACK (shutdown_button_clicked), login_window); + +- entry = glade_xml_get_widget (login_window->priv->xml, "auth-prompt-entry"); +- /* Only change the invisible character if it '*' otherwise assume it is OK */ +- if ('*' == gtk_entry_get_invisible_char (GTK_ENTRY (entry))) { +- gunichar invisible_char; +- invisible_char = INVISIBLE_CHAR_BLACK_CIRCLE; +- gtk_entry_set_invisible_char (GTK_ENTRY (entry), invisible_char); +- } +- + create_computer_info (login_window); + + box = glade_xml_get_widget (login_window->priv->xml, "computer-info-event-box"); +@@ -1908,9 +2166,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, +@@ -1963,6 +2221,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 = glade_xml_get_widget (login_window->priv->xml, "buttonbox"); ++ for (node = action_list; node != NULL; node = node->next) { ++ GtkAction *action; ++ GtkWidget *button; ++ ++ action = node->data; ++ ++ button = create_button_from_action (action); ++ gtk_container_add (GTK_CONTAINER (box), button); ++ } ++ ++ g_list_free (action_list); ++} ++ ++void ++gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window, ++ GdmGreeterExtension *extension) ++{ ++ char *name; ++ char *description; ++ char *service_name; ++ GtkActionGroup *actions; ++ ++ g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window)); ++ g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION (extension)); ++ ++ if (!GDM_IS_CONVERSATION (extension)) { ++ return; ++ } ++ ++ actions = gdm_conversation_get_actions (GDM_CONVERSATION (extension)); ++ ++ create_buttons_for_actions (login_window, actions); ++ hide_task_actions (GDM_TASK (extension)); ++ ++ g_object_unref (actions); ++ ++ g_signal_connect_swapped (GDM_CONVERSATION (extension), ++ "answer", ++ G_CALLBACK (on_conversation_answer), ++ login_window); ++ ++ 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.glade b/gui/simple-greeter/gdm-greeter-login-window.glade +index 3d9e2ee..76ab718 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.glade ++++ b/gui/simple-greeter/gdm-greeter-login-window.glade +@@ -611,30 +611,29 @@ + + + +- +- True +- +- False +- False +- GTK_JUSTIFY_CENTER +- True +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- True +- True +- ++ ++ True ++ ++ False ++ False ++ GTK_JUSTIFY_CENTER ++ True ++ False ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ + +- + + + True +@@ -660,20 +659,6 @@ + 0 + + +- +- True +- 0 +- 0 +- Tue, 18 Nov 2008 21:55:38 GMT +- +- +- 0 +- True +- True +- +- +- +- + + True + 0 +@@ -686,56 +671,12 @@ + False + + +- +- +- 0 +- True +- True +- +- +- +- +- +- True +- False +- 6 +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- False +- False +- +- + + +- +- True +- True +- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK +- True +- True +- 0 +- +- True +- True ++ ++ 0 ++ 0 ++ Tue, 18 Nov 2008 21:55:38 GMT + + + 0 +@@ -744,46 +685,21 @@ + + + +- +- +- + + + 0 +- False +- False ++ True ++ True + + + + +- ++ + True + False + 0 +- + +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- True +- True +- ++ + + + +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 ++#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-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 ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "gdm-greeter-extension.h" ++#include "gdm-greeter-plugin.h" ++ ++#define GDM_GREETER_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_PLUGIN, GdmGreeterPluginPrivate)) ++ ++enum { ++ PROP_0, ++ PROP_FILENAME, ++}; ++ ++enum { ++ LOADED, ++ LOAD_FAILED, ++ UNLOADED, ++ LAST_SIGNAL ++}; ++ ++struct _GdmGreeterPluginPrivate { ++ GObject parent; ++ ++ GModule *module; ++ char *filename; ++ ++ GdmGreeterExtension *extension; ++}; ++ ++static void gdm_greeter_plugin_finalize (GObject *object); ++ ++static guint signals[LAST_SIGNAL] = { 0 }; ++ ++G_DEFINE_TYPE (GdmGreeterPlugin, gdm_greeter_plugin, G_TYPE_OBJECT) ++ ++static void ++gdm_greeter_plugin_set_property (GObject *object, ++ guint param_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GdmGreeterPlugin *plugin; ++ ++ plugin = GDM_GREETER_PLUGIN (object); ++ switch (param_id) { ++ case PROP_FILENAME: ++ plugin->priv->filename = g_strdup (g_value_get_string (value)); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); ++ break; ++ } ++} ++ ++static void ++gdm_greeter_plugin_get_property (GObject *object, ++ guint param_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GdmGreeterPlugin *plugin; ++ ++ plugin = GDM_GREETER_PLUGIN (object); ++ ++ switch (param_id) { ++ case PROP_FILENAME: ++ g_value_set_string (value, plugin->priv->filename); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); ++ break; ++ } ++} ++ ++static void ++gdm_greeter_plugin_class_init (GdmGreeterPluginClass *class) ++{ ++ GObjectClass *gobject_class; ++ ++ gobject_class = G_OBJECT_CLASS (class); ++ ++ gobject_class->set_property = gdm_greeter_plugin_set_property; ++ gobject_class->get_property = gdm_greeter_plugin_get_property; ++ gobject_class->finalize = gdm_greeter_plugin_finalize; ++ ++ g_object_class_install_property (gobject_class, ++ PROP_FILENAME, ++ g_param_spec_string ("filename", ++ "Filename", ++ "The full path to the plugin.", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); ++ ++ signals [LOADED] = ++ g_signal_new ("loaded", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmGreeterPluginClass, loaded), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ signals [LOAD_FAILED] = ++ g_signal_new ("load-failed", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmGreeterPluginClass, load_failed), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ signals [UNLOADED] = ++ g_signal_new ("unloaded", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmGreeterPluginClass, unloaded), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ g_type_class_add_private (class, sizeof (GdmGreeterPluginPrivate)); ++} ++ ++GdmGreeterPlugin * ++gdm_greeter_plugin_new (const char *filename) ++{ ++ GObject *object; ++ ++ object = g_object_new (GDM_TYPE_GREETER_PLUGIN, ++ "filename", filename, NULL); ++ ++ return GDM_GREETER_PLUGIN (object); ++} ++ ++void ++gdm_greeter_plugin_load (GdmGreeterPlugin *plugin) ++{ ++ GModule *module; ++ GdmGreeterExtension *extension; ++ union { ++ gpointer symbol; ++ GdmGreeterPluginGetExtensionFunc invoke; ++ } get_extension; ++ ++ ++ module = g_module_open (plugin->priv->filename, G_MODULE_BIND_LOCAL); ++ ++ if (module == NULL) { ++ g_warning ("plugin %s couldn't be opened: %s", ++ plugin->priv->filename, ++ g_module_error ()); ++ g_signal_emit (plugin, signals [LOAD_FAILED], 0); ++ return; ++ } ++ ++ if (!g_module_symbol (module, ++ "gdm_greeter_plugin_get_extension", ++ &get_extension.symbol) || ++ !get_extension.symbol) { ++ g_warning ("plugin %s lacks gdm_greeter_plugin_get_extension()", ++ plugin->priv->filename); ++ g_module_close (module); ++ g_signal_emit (plugin, signals [LOAD_FAILED], 0); ++ return; ++ } ++ ++ extension = get_extension.invoke (); ++ ++ if (!extension) { ++ g_warning ("plugin %s didn't return extension when asked", ++ plugin->priv->filename); ++ g_module_close (module); ++ g_signal_emit (plugin, signals [LOAD_FAILED], 0); ++ } ++ ++ if (!GDM_IS_GREETER_EXTENSION (extension)) { ++ g_warning ("plugin %s returned bogus extension when asked", ++ plugin->priv->filename); ++ g_module_close (module); ++ g_signal_emit (plugin, signals [LOAD_FAILED], 0); ++ } ++ ++ plugin->priv->module = module; ++ plugin->priv->extension = extension; ++ ++ g_signal_emit (plugin, signals [LOADED], 0); ++} ++ ++void ++gdm_greeter_plugin_unload (GdmGreeterPlugin *plugin) ++{ ++ if (plugin->priv->extension != NULL) { ++ g_object_unref (plugin->priv->extension); ++ plugin->priv->extension = NULL; ++ } ++ ++ if (plugin->priv->module != NULL) { ++ g_module_close (plugin->priv->module); ++ plugin->priv->module = NULL; ++ } ++} ++ ++const char * ++gdm_greeter_plugin_get_filename (GdmGreeterPlugin *plugin) ++{ ++ return plugin->priv->filename; ++} ++ ++GdmGreeterExtension * ++gdm_greeter_plugin_get_extension (GdmGreeterPlugin *plugin) ++{ ++ return g_object_ref (plugin->priv->extension); ++} ++ ++static void ++gdm_greeter_plugin_init (GdmGreeterPlugin *plugin) ++{ ++ plugin->priv = GDM_GREETER_PLUGIN_GET_PRIVATE (plugin); ++} ++ ++static void ++gdm_greeter_plugin_finalize (GObject *object) ++{ ++ GdmGreeterPlugin *plugin; ++ ++ plugin = GDM_GREETER_PLUGIN (object); ++ ++ gdm_greeter_plugin_unload (plugin); ++} ++ +diff --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 ++#include ++ ++#include "gdm-greeter-extension.h" ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_GREETER_PLUGIN (gdm_greeter_plugin_get_type ()) ++#define GDM_GREETER_PLUGIN(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_GREETER_PLUGIN, GdmGreeterPlugin)) ++#define GDM_IS_GREETER_PLUGIN(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_GREETER_PLUGIN)) ++ ++typedef struct _GdmGreeterPlugin GdmGreeterPlugin; ++typedef struct _GdmGreeterPluginPrivate GdmGreeterPluginPrivate; ++typedef struct _GdmGreeterPluginClass GdmGreeterPluginClass; ++ ++struct _GdmGreeterPlugin ++{ ++ GObject parent; ++ GdmGreeterPluginPrivate *priv; ++}; ++ ++struct _GdmGreeterPluginClass ++{ ++ GObjectClass parent_class; ++ ++ void (* loaded) (GdmGreeterPlugin *plugin); ++ void (* load_failed) (GdmGreeterPlugin *plugin); ++ void (* unloaded) (GdmGreeterPlugin *plugin); ++}; ++ ++GType gdm_greeter_plugin_get_type (void) G_GNUC_CONST; ++GdmGreeterPlugin *gdm_greeter_plugin_new (const char *filename); ++void gdm_greeter_plugin_load (GdmGreeterPlugin *plugin); ++void gdm_greeter_plugin_unload (GdmGreeterPlugin *plugin); ++const char *gdm_greeter_plugin_get_filename (GdmGreeterPlugin *plugin); ++GdmGreeterExtension *gdm_greeter_plugin_get_extension (GdmGreeterPlugin *plugin); ++ ++G_END_DECLS ++ ++#endif +diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c +index 714daa1..adbed6f 100644 +--- a/gui/simple-greeter/gdm-greeter-session.c ++++ b/gui/simple-greeter/gdm-greeter-session.c +@@ -38,6 +38,8 @@ + #include "gdm-greeter-panel.h" + #include "gdm-greeter-login-window.h" + ++#include "gdm-plugin-manager.h" ++ + #include "gdm-profile.h" + + #define GDM_GREETER_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_SESSION, GdmGreeterSessionPrivate)) +@@ -45,9 +47,11 @@ + struct GdmGreeterSessionPrivate + { + GdmGreeterClient *client; ++ GdmPluginManager *plugin_manager; + + GtkWidget *login_window; + GtkWidget *panel; ++ + }; + + enum { +@@ -91,7 +95,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 +@@ -154,10 +170,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 +@@ -270,7 +287,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 +@@ -282,9 +298,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 +@@ -436,8 +453,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; +@@ -547,6 +562,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); +@@ -575,6 +648,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); +@@ -609,6 +686,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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "gdm-plugin-manager.h" ++#include "gdm-greeter-extension.h" ++ ++#define GDM_PLUGIN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerPrivate)) ++ ++typedef struct ++{ ++ GModule *module; ++ char *filename; ++ GdmGreeterExtension *extension; ++} GdmPluginManagerPlugin; ++ ++typedef struct ++{ ++ GdmPluginManager *manager; ++ GCancellable *cancellable; ++} GdmPluginManagerOperation; ++ ++struct GdmPluginManagerPrivate ++{ ++ GHashTable *plugins; ++ ++ GFileMonitor *plugin_dir_monitor; ++ GList *pending_operations; ++}; ++ ++enum { ++ PLUGINS_LOADED, ++ PLUGIN_ADDED, ++ PLUGIN_REMOVED, ++ LAST_SIGNAL ++}; ++ ++static guint signals [LAST_SIGNAL] = { 0, }; ++ ++static void gdm_plugin_manager_class_init (GdmPluginManagerClass *klass); ++static void gdm_plugin_manager_init (GdmPluginManager *plugin_manager); ++static void gdm_plugin_manager_finalize (GObject *object); ++ ++static GObject *plugin_manager_object = NULL; ++ ++G_DEFINE_TYPE (GdmPluginManager, gdm_plugin_manager, G_TYPE_OBJECT) ++ ++static GdmPluginManagerOperation * ++start_operation (GdmPluginManager *manager) ++{ ++ GdmPluginManagerOperation *operation; ++ ++ operation = g_new0 (GdmPluginManagerOperation, 1); ++ operation->cancellable = g_cancellable_new (); ++ operation->manager = manager; ++ ++ return operation; ++} ++ ++static void ++free_operation (GdmPluginManagerOperation *operation) ++{ ++ if (operation->cancellable != NULL) { ++ g_object_unref (operation->cancellable); ++ operation->cancellable = NULL; ++ } ++ g_free (operation); ++} ++ ++static void ++cancel_operation (GdmPluginManagerOperation *operation) ++{ ++ if (operation->cancellable != NULL && ++ !g_cancellable_is_cancelled (operation->cancellable)) { ++ g_cancellable_cancel (operation->cancellable); ++ } ++ ++ free_operation (operation); ++} ++ ++static void ++gdm_plugin_manager_track_operation (GdmPluginManager *manager, ++ GdmPluginManagerOperation *operation) ++{ ++ manager->priv->pending_operations = ++ g_list_prepend (manager->priv->pending_operations, operation); ++} ++ ++static void ++gdm_plugin_manager_untrack_operation (GdmPluginManager *manager, ++ GdmPluginManagerOperation *operation) ++{ ++ manager->priv->pending_operations = ++ g_list_remove (manager->priv->pending_operations, operation); ++} ++ ++static void ++gdm_plugin_manager_cancel_pending_operations (GdmPluginManager *manager) ++{ ++ GList *node; ++ ++ node = manager->priv->pending_operations; ++ while (node != NULL) { ++ GList *next_node; ++ GdmPluginManagerOperation *operation; ++ ++ operation = node->data; ++ next_node = node->next; ++ ++ cancel_operation (operation); ++ manager->priv->pending_operations = ++ g_list_delete_link (manager->priv->pending_operations, ++ node); ++ ++ node = next_node; ++ } ++} ++ ++static void ++gdm_plugin_manager_class_init (GdmPluginManagerClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = gdm_plugin_manager_finalize; ++ ++ signals [PLUGINS_LOADED] = ++ g_signal_new ("plugins-loaded", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmPluginManagerClass, plugins_loaded), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ signals [PLUGIN_ADDED] = ++ g_signal_new ("plugin-added", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmPluginManagerClass, plugin_added), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, GDM_TYPE_GREETER_PLUGIN); ++ signals [PLUGIN_REMOVED] = ++ g_signal_new ("plugin-removed", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmPluginManagerClass, plugin_removed), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, GDM_TYPE_GREETER_PLUGIN); ++ ++ g_type_class_add_private (klass, sizeof (GdmPluginManagerPrivate)); ++} ++ ++static void ++on_plugin_loaded (GdmPluginManager *manager, ++ GdmGreeterPlugin *plugin) ++{ ++ g_debug ("GdmPluginManager: plugin '%s' loaded.", ++ gdm_greeter_plugin_get_filename (plugin)); ++ g_signal_emit (manager, signals [PLUGIN_ADDED], 0, plugin); ++} ++ ++static void ++on_plugin_load_failed (GdmPluginManager *manager, ++ GdmGreeterPlugin *plugin) ++{ ++ const char *filename; ++ ++ g_debug ("GdmPluginManager: plugin '%s' could not be loaded.", ++ gdm_greeter_plugin_get_filename (plugin)); ++ filename = gdm_greeter_plugin_get_filename (plugin); ++ g_hash_table_remove (manager->priv->plugins, filename); ++} ++ ++static void ++on_plugin_unloaded (GdmPluginManager *manager, ++ GdmGreeterPlugin *plugin) ++{ ++ const char *filename; ++ ++ filename = gdm_greeter_plugin_get_filename (plugin); ++ g_hash_table_remove (manager->priv->plugins, filename); ++} ++ ++static void ++load_plugin (GdmPluginManager *manager, ++ const char *filename) ++{ ++ GdmGreeterPlugin *plugin; ++ ++ g_debug ("GdmPluginManager: loading plugin '%s'", filename); ++ ++ plugin = gdm_greeter_plugin_new (filename); ++ ++ g_signal_connect_swapped (plugin, "loaded", ++ G_CALLBACK (on_plugin_loaded), ++ manager); ++ g_signal_connect_swapped (plugin, "load-failed", ++ G_CALLBACK (on_plugin_load_failed), ++ manager); ++ g_signal_connect_swapped (plugin, "unloaded", ++ G_CALLBACK (on_plugin_unloaded), ++ manager); ++ g_hash_table_insert (manager->priv->plugins, ++ g_strdup (filename), plugin); ++ ++ gdm_greeter_plugin_load (plugin); ++} ++ ++static void ++on_plugin_info_read (GFileEnumerator *enumerator, ++ GAsyncResult *result, ++ GdmPluginManagerOperation *operation) ++{ ++ GdmPluginManager *manager; ++ GFile *plugin_dir_file; ++ GList *file_list, *node; ++ GError *error; ++ ++ manager = operation->manager; ++ error = NULL; ++ file_list = g_file_enumerator_next_files_finish (enumerator, ++ result, &error); ++ plugin_dir_file = g_file_enumerator_get_container (enumerator); ++ if (error != NULL) { ++ char *plugin_dir; ++ ++ plugin_dir = g_file_get_parse_name (plugin_dir_file); ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ++ g_debug ("GdmPluginManager: Cancelled reading plugin directory %s", ++ plugin_dir); ++ } else { ++ g_warning ("GdmPluginManager: Unable to read plugin directory %s: %s", ++ plugin_dir, error->message); ++ } ++ g_free (plugin_dir); ++ g_error_free (error); ++ g_object_unref (plugin_dir_file); ++ gdm_plugin_manager_untrack_operation (manager, operation); ++ return; ++ } ++ ++#ifndef PLUGIN_ORDERING_FIGURED_OUT ++ node = file_list; ++ while (node != NULL) { ++ GFileInfo *info; ++ GFile *file; ++ char *path; ++ GList *next_node; ++ ++ next_node = node->next; ++ ++ info = (GFileInfo *) node->data; ++ ++ file = g_file_get_child (plugin_dir_file, ++ g_file_info_get_name (info)); ++ path = g_file_get_path (file); ++ ++ if (g_str_has_suffix (path, "password.so")) { ++ file_list = g_list_delete_link (file_list, node); ++ file_list = g_list_prepend (file_list, info); ++ next_node = NULL; ++ } ++ g_free (path); ++ g_object_unref (file); ++ ++ node = next_node; ++ } ++#endif ++ ++ node = file_list; ++ while (node != NULL) { ++ GFileInfo *info; ++ GFile *file; ++ char *path; ++ ++ info = (GFileInfo *) node->data; ++ ++ file = g_file_get_child (plugin_dir_file, ++ g_file_info_get_name (info)); ++ path = g_file_get_path (file); ++ ++ if (g_str_has_suffix (path, G_MODULE_SUFFIX)) { ++ load_plugin (manager, path); ++ } ++ g_free (path); ++ g_object_unref (file); ++ ++ node = node->next; ++ } ++ g_object_unref (plugin_dir_file); ++ ++ gdm_plugin_manager_untrack_operation (manager, operation); ++ g_signal_emit (manager, signals [PLUGINS_LOADED], 0); ++ ++ g_list_free (file_list); ++} ++ ++static void ++on_plugin_dir_opened (GFile *plugin_dir_file, ++ GAsyncResult *result, ++ GdmPluginManagerOperation *open_operation) ++{ ++ GdmPluginManager *manager; ++ GFileEnumerator *enumerator; ++ GError *error; ++ GdmPluginManagerOperation *operation; ++ ++ manager = open_operation->manager; ++ gdm_plugin_manager_untrack_operation (manager, open_operation); ++ ++ error = NULL; ++ enumerator = g_file_enumerate_children_finish (plugin_dir_file, ++ result, &error); ++ ++ if (enumerator == NULL) { ++ char *plugin_dir; ++ ++ plugin_dir = g_file_get_parse_name (plugin_dir_file); ++ ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ++ g_debug ("GdmPluginManager: Cancelled opening plugin directory %s", ++ plugin_dir); ++ } else { ++ g_warning ("GdmPluginManager: Unable to open plugin directory %s: %s", ++ plugin_dir, error->message); ++ } ++ g_free (plugin_dir); ++ g_error_free (error); ++ return; ++ } ++ ++ operation = start_operation (manager); ++ ++ g_file_enumerator_next_files_async (enumerator, G_MAXINT, ++ G_PRIORITY_DEFAULT, ++ operation->cancellable, ++ (GAsyncReadyCallback) ++ on_plugin_info_read, ++ operation); ++ ++ gdm_plugin_manager_track_operation (manager, operation); ++} ++ ++static void ++load_plugins_in_dir (GdmPluginManager *manager, ++ const char *plugin_dir) ++{ ++ GFile *plugin_dir_file; ++ GdmPluginManagerOperation *operation; ++ ++ g_debug ("GdmPluginManager: loading plugins in dir '%s'", plugin_dir); ++ ++ operation = start_operation (manager); ++ plugin_dir_file = g_file_new_for_path (plugin_dir); ++ g_file_enumerate_children_async (plugin_dir_file, "standard::*", ++ G_FILE_QUERY_INFO_NONE, ++ G_PRIORITY_DEFAULT, ++ operation->cancellable, ++ (GAsyncReadyCallback) ++ on_plugin_dir_opened, ++ operation); ++ g_object_unref (plugin_dir_file); ++ gdm_plugin_manager_track_operation (manager, operation); ++} ++ ++static void ++on_plugin_dir_changed (GFileMonitor *monitor, ++ GFile *file, ++ GFile *other_file, ++ GFileMonitorEvent event_type, ++ GdmPluginManagerOperation *operation) ++{ ++} ++ ++static void ++watch_plugin_dir (GdmPluginManager *manager, ++ const char *plugin_dir) ++{ ++ ++ GdmPluginManagerOperation *operation; ++ GFile *file; ++ GError *error; ++ ++ operation = start_operation (manager); ++ ++ file = g_file_new_for_path (plugin_dir); ++ manager->priv->plugin_dir_monitor = g_file_monitor_directory (file, ++ G_FILE_MONITOR_NONE, ++ operation->cancellable, ++ &error); ++ if (manager->priv->plugin_dir_monitor != NULL) { ++ g_signal_connect (manager->priv->plugin_dir_monitor, ++ "changed", ++ G_CALLBACK (on_plugin_dir_changed), ++ operation); ++ gdm_plugin_manager_track_operation (manager, operation); ++ } else { ++ g_warning ("Unable to monitor %s: %s", ++ plugin_dir, error->message); ++ g_error_free (error); ++ free_operation (operation); ++ } ++ g_object_unref (file); ++} ++ ++static void ++gdm_plugin_manager_init (GdmPluginManager *manager) ++{ ++ manager->priv = GDM_PLUGIN_MANAGER_GET_PRIVATE (manager); ++ ++ manager->priv->plugins = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ g_object_unref); ++ watch_plugin_dir (manager, GDM_SIMPLE_GREETER_PLUGINS_DIR); ++ load_plugins_in_dir (manager, GDM_SIMPLE_GREETER_PLUGINS_DIR); ++} ++ ++static void ++gdm_plugin_manager_finalize (GObject *object) ++{ ++ GdmPluginManager *manager; ++ ++ manager = GDM_PLUGIN_MANAGER (object); ++ ++ g_hash_table_destroy (manager->priv->plugins); ++ g_file_monitor_cancel (manager->priv->plugin_dir_monitor); ++ ++ gdm_plugin_manager_cancel_pending_operations (manager); ++ ++ G_OBJECT_CLASS (gdm_plugin_manager_parent_class)->finalize (object); ++} ++ ++GdmPluginManager * ++gdm_plugin_manager_ref_default (void) ++{ ++ if (plugin_manager_object != NULL) { ++ g_object_ref (plugin_manager_object); ++ } else { ++ plugin_manager_object = g_object_new (GDM_TYPE_PLUGIN_MANAGER, NULL); ++ g_object_add_weak_pointer (plugin_manager_object, ++ (gpointer *) &plugin_manager_object); ++ } ++ ++ return GDM_PLUGIN_MANAGER (plugin_manager_object); ++} ++ ++GdmGreeterPlugin * ++gdm_plugin_manager_get_plugin (GdmPluginManager *manager, ++ const char *name) ++{ ++ return g_hash_table_lookup (manager->priv->plugins, name); ++} +diff --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 ++ * ++ */ ++ ++#ifndef __GDM_PLUGIN_MANAGER_H ++#define __GDM_PLUGIN_MANAGER_H ++ ++#include ++ ++#include "gdm-greeter-plugin.h" ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_PLUGIN_MANAGER (gdm_plugin_manager_get_type ()) ++#define GDM_PLUGIN_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManager)) ++#define GDM_PLUGIN_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerClass)) ++#define GDM_IS_PLUGIN_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_PLUGIN_MANAGER)) ++#define GDM_IS_PLUGIN_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_PLUGIN_MANAGER)) ++#define GDM_PLUGIN_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerClass)) ++ ++typedef struct GdmPluginManagerPrivate GdmPluginManagerPrivate; ++ ++typedef struct ++{ ++ GObject parent; ++ GdmPluginManagerPrivate *priv; ++} GdmPluginManager; ++ ++typedef struct ++{ ++ GObjectClass parent_class; ++ ++ void (* plugins_loaded) (GdmPluginManager *plugin_manager); ++ void (* plugin_added) (GdmPluginManager *plugin_manager, ++ GdmGreeterPlugin *plugin); ++ void (* plugin_removed) (GdmPluginManager *plugin_manager, ++ GdmGreeterPlugin *plugin); ++} GdmPluginManagerClass; ++ ++GType gdm_plugin_manager_get_type (void); ++ ++GdmPluginManager *gdm_plugin_manager_ref_default (void); ++ ++GdmGreeterPlugin *gdm_plugin_manager_get_plugin (GdmPluginManager *plugin, ++ const char *name); ++ ++G_END_DECLS ++ ++#endif /* __GDM_PLUGIN_MANAGER_H */ +diff --git a/gui/simple-greeter/gdm-task-list.c b/gui/simple-greeter/gdm-task-list.c +index e0fd3d4..25831a6 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,161 @@ 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 (G_OBJECT (button), "draw-indicator", FALSE, NULL); ++ g_object_set_data (G_OBJECT (button), "gdm-task", task); ++ g_signal_connect_swapped (button, "toggled", ++ G_CALLBACK (on_task_toggled), ++ task_list); ++ ++ gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE); ++ gtk_widget_set_sensitive (button, gdm_task_is_enabled (task)); ++ ++ g_signal_connect_swapped (G_OBJECT (task), "enabled", ++ G_CALLBACK (on_task_enabled), ++ task_list); ++ ++ g_signal_connect_swapped (G_OBJECT (task), "disabled", ++ G_CALLBACK (on_task_disabled), ++ task_list); ++ ++ icon = gdm_task_get_icon (task); ++ image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_SMALL_TOOLBAR); ++ g_object_unref (icon); + +- 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 (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), task->radio_button); ++ gtk_container_add (GTK_CONTAINER (task_list->priv->box), button); + task_list->priv->tasks = g_list_append (task_list->priv->tasks, task); ++ ++ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) { ++ g_signal_emit (task_list, signals[ACTIVATED], 0, task); ++ } + } + + static void +@@ -122,9 +223,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 +233,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); +@@ -179,20 +277,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 ++#include + #include + ++#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 ++ * ++ */ ++ ++#include ++#include ++ ++#include ++ ++#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 ++ */ ++ ++ ++#ifndef __GDM_CONVERSATION_H ++#define __GDM_CONVERSATION_H ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_CONVERSATION (gdm_conversation_get_type ()) ++#define GDM_CONVERSATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_CONVERSATION, GdmConversation)) ++#define GDM_CONVERSATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_CONVERSATION, GdmConversationIface)) ++#define GDM_IS_CONVERSATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_CONVERSATION)) ++#define GDM_CONVERSATION_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_CONVERSATION, GdmConversationIface)) ++ ++#define GDM_CONVERSATION_DEFAULT_ACTION "default-action" ++ ++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 ++ * ++ */ ++ ++#include ++#include ++ ++#include "gdm-greeter-extension.h" ++ ++enum { ++ LOADED, ++ LOAD_FAILED, ++ LAST_SIGNAL ++}; ++ ++static guint signals [LAST_SIGNAL] = { 0, }; ++ ++static void gdm_greeter_extension_class_init (gpointer g_iface); ++ ++GType ++gdm_greeter_extension_get_type (void) ++{ ++ static GType greeter_extension_type = 0; ++ ++ if (!greeter_extension_type) { ++ greeter_extension_type = g_type_register_static_simple (G_TYPE_INTERFACE, ++ "GdmGreeterExtension", ++ sizeof (GdmGreeterExtensionIface), ++ (GClassInitFunc) gdm_greeter_extension_class_init, ++ 0, NULL, 0); ++ ++ g_type_interface_add_prerequisite (greeter_extension_type, G_TYPE_OBJECT); ++ } ++ ++ return greeter_extension_type; ++} ++ ++static void ++gdm_greeter_extension_class_init (gpointer g_iface) ++{ ++ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); ++ ++ signals [LOADED] = ++ g_signal_new ("loaded", ++ iface_type, ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (GdmGreeterExtensionIface, loaded), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ signals [LOADED] = ++ g_signal_new ("load_failed", ++ iface_type, ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (GdmGreeterExtensionIface, load_failed), ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, ++ 1, G_TYPE_POINTER); ++} ++ ++void ++gdm_greeter_extension_loaded (GdmGreeterExtension *extension) ++{ ++ g_signal_emit (extension, signals [LOADED], 0); ++} ++ ++void ++gdm_greeter_extension_load_failed (GdmGreeterExtension *extension, ++ GError *error) ++{ ++ g_signal_emit (extension, signals [LOAD_FAILED], 0, error); ++} +diff --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 ++ ++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 ++ * ++ */ ++ ++#include ++#include ++ ++#include "gdm-task.h" ++ ++enum { ++ ENABLED, ++ DISABLED, ++ LAST_SIGNAL ++}; ++ ++static guint signals [LAST_SIGNAL] = { 0, }; ++static void gdm_task_class_init (gpointer g_iface); ++ ++GType ++gdm_task_get_type (void) ++{ ++ static GType task_type = 0; ++ ++ if (!task_type) { ++ task_type = g_type_register_static_simple (G_TYPE_INTERFACE, ++ "GdmTask", ++ sizeof (GdmTaskIface), ++ (GClassInitFunc) gdm_task_class_init, ++ 0, NULL, 0); ++ ++ g_type_interface_add_prerequisite (task_type, G_TYPE_OBJECT); ++ } ++ ++ return task_type; ++} ++ ++GIcon * ++gdm_task_get_icon (GdmTask *task) ++{ ++ return GDM_TASK_GET_IFACE (task)->get_icon (task); ++} ++ ++char * ++gdm_task_get_description (GdmTask *task) ++{ ++ return GDM_TASK_GET_IFACE (task)->get_description (task); ++} ++ ++char * ++gdm_task_get_name (GdmTask *task) ++{ ++ return GDM_TASK_GET_IFACE (task)->get_name (task); ++} ++ ++void ++gdm_task_set_enabled (GdmTask *task, ++ gboolean should_enable) ++{ ++ g_object_set_data (G_OBJECT (task), "gdm-task-is-disabled", GINT_TO_POINTER (!should_enable)); ++ ++ if (should_enable) { ++ g_signal_emit (G_OBJECT (task), signals [ENABLED], 0); ++ } else { ++ g_signal_emit (G_OBJECT (task), signals [DISABLED], 0); ++ } ++} ++ ++gboolean ++gdm_task_is_enabled (GdmTask *task) ++{ ++ return !g_object_get_data (G_OBJECT (task), "gdm-task-is-disabled"); ++} ++ ++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 ++ */ ++ ++ ++#ifndef __GDM_TASK_H ++#define __GDM_TASK_H ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_TASK (gdm_task_get_type ()) ++#define GDM_TASK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_TASK, GdmTask)) ++#define GDM_TASK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_TASK, GdmTaskIface)) ++#define GDM_IS_TASK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_TASK)) ++#define GDM_TASK_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_TASK, GdmTaskIface)) ++ ++typedef struct _GdmTask GdmTask; ++typedef struct _GdmTaskIface GdmTaskIface; ++ ++struct _GdmTaskIface ++{ ++ GTypeInterface base_iface; ++ ++ /* methods */ ++ GIcon * (* get_icon) (GdmTask *task); ++ char * (* get_description) (GdmTask *task); ++ char * (* get_name) (GdmTask *task); ++ /* 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..fae73be +--- /dev/null ++++ b/gui/simple-greeter/plugins/password/gdm-password-extension.c +@@ -0,0 +1,316 @@ ++/* ++ * Copyright (C) 2009 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Written By: Ray Strode ++ * ++ */ ++ ++#include ++#include "gdm-password-extension.h" ++#include "gdm-conversation.h" ++#include "gdm-task.h" ++ ++#include ++#include ++#include ++ ++struct _GdmPasswordExtensionPrivate ++{ ++ GIcon *icon; ++ GtkWidget *page; ++ GtkActionGroup *actions; ++ ++ GtkWidget *message_label; ++ GtkWidget *prompt_label; ++ GtkWidget *prompt_entry; ++ ++ guint answer_pending : 1; ++}; ++ ++static void gdm_password_extension_finalize (GObject *object); ++ ++static void gdm_task_iface_init (GdmTaskIface *iface); ++static void gdm_conversation_iface_init (GdmConversationIface *iface); ++static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface); ++ ++G_DEFINE_TYPE_WITH_CODE (GdmPasswordExtension, ++ gdm_password_extension, ++ G_TYPE_OBJECT, ++ G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION, ++ gdm_greeter_extension_iface_init) ++ G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK, ++ gdm_task_iface_init) ++ G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION, ++ gdm_conversation_iface_init)); ++ ++static void ++gdm_password_extension_set_message (GdmConversation *conversation, ++ const char *message) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ gtk_widget_show (extension->priv->message_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message); ++} ++ ++static void ++gdm_password_extension_ask_question (GdmConversation *conversation, ++ const char *message) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ gtk_widget_show (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++ gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE); ++ gtk_widget_show (extension->priv->prompt_entry); ++ gtk_widget_grab_focus (extension->priv->prompt_entry); ++ extension->priv->answer_pending = TRUE; ++} ++ ++static void ++gdm_password_extension_ask_secret (GdmConversation *conversation, ++ const char *message) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ gtk_widget_show (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message); ++ gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++ gtk_widget_show (extension->priv->prompt_entry); ++ gtk_widget_grab_focus (extension->priv->prompt_entry); ++ extension->priv->answer_pending = TRUE; ++} ++ ++static void ++gdm_password_extension_reset (GdmConversation *conversation) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ gtk_widget_hide (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), ""); ++ ++ gtk_widget_hide (extension->priv->prompt_entry); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++ gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE); ++ extension->priv->answer_pending = FALSE; ++ ++ gdm_task_set_enabled (GDM_TASK (conversation), FALSE); ++} ++ ++static void ++gdm_password_extension_set_ready (GdmConversation *conversation) ++{ ++ gdm_task_set_enabled (GDM_TASK (conversation), TRUE); ++} ++ ++char * ++gdm_password_extension_get_service_name (GdmConversation *conversation) ++{ ++ return g_strdup (PAMSERVICENAME); ++} ++ ++GtkWidget * ++gdm_password_extension_get_page (GdmConversation *conversation) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ return extension->priv->page; ++} ++ ++GtkActionGroup * ++gdm_password_extension_get_actions (GdmConversation *conversation) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ return g_object_ref (extension->priv->actions); ++} ++ ++void ++gdm_password_extension_request_answer (GdmConversation *conversation) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ const char *text; ++ ++ if (!extension->priv->answer_pending) { ++ gdm_conversation_answer (conversation, NULL); ++ return; ++ } ++ ++ extension->priv->answer_pending = FALSE; ++ text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry)); ++ gdm_conversation_answer (conversation, text); ++ ++ gtk_widget_hide (extension->priv->prompt_entry); ++ gtk_widget_hide (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), ""); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++} ++ ++gboolean ++gdm_password_extension_focus (GdmConversation *conversation) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation); ++ if (!extension->priv->answer_pending) { ++ gdm_conversation_answer (conversation, NULL); ++ return FALSE; ++ } ++ ++ gtk_widget_grab_focus (extension->priv->prompt_entry); ++ return TRUE; ++} ++ ++GIcon * ++gdm_password_extension_get_icon (GdmTask *task) ++{ ++ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (task); ++ return g_object_ref (extension->priv->icon); ++} ++ ++char * ++gdm_password_extension_get_name (GdmTask *task) ++{ ++ return g_strdup (_("Password Authentication")); ++} ++ ++char * ++gdm_password_extension_get_description (GdmTask *task) ++{ ++ return g_strdup (_("Log into session with username and password")); ++} ++ ++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"), ++ _("Log into the currently selected sesson"), ++ NULL); ++ g_signal_connect_swapped (action, "activate", ++ G_CALLBACK (on_activate_log_in), extension); ++ g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL); ++ gtk_action_group_add_action (extension->priv->actions, ++ action); ++} ++ ++static void ++gdm_password_extension_init (GdmPasswordExtension *extension) ++{ ++ extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension, ++ GDM_TYPE_PASSWORD_EXTENSION, ++ GdmPasswordExtensionPrivate); ++ ++ extension->priv->icon = g_themed_icon_new ("dialog-password"); ++ create_page (extension); ++ create_actions (extension); ++ ++ gdm_password_extension_reset (GDM_CONVERSATION (extension)); ++} +diff --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 ++ */ ++ ++#ifndef __GDM_PASSWORD_EXTENSION_H ++#define __GDM_PASSWORD_EXTENSION_H ++ ++#include ++#include "gdm-greeter-extension.h" ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_PASSWORD_EXTENSION (gdm_password_extension_get_type ()) ++#define GDM_PASSWORD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtension)) ++#define GDM_PASSWORD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtensionClass)) ++#define GDM_IS_PASSWORD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_PASSWORD_EXTENSION)) ++#define GDM_IS_PASSWORD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_PASSWORD_EXTENSION)) ++#define GDM_PASSWORD_EXTENSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtensionClass)) ++ ++typedef struct _GdmPasswordExtensionPrivate GdmPasswordExtensionPrivate; ++ ++typedef struct ++{ ++ GObject parent; ++ GdmPasswordExtensionPrivate *priv; ++} GdmPasswordExtension; ++ ++typedef struct ++{ ++ GObjectClass parent_class; ++} GdmPasswordExtensionClass; ++ ++GType gdm_password_extension_get_type (void); ++ ++GdmPasswordExtension *gdm_password_extension_new (void); ++ ++G_END_DECLS ++ ++#endif /* GDM_PASSWORD_EXTENSION_H */ +diff --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 @@ ++ ++ ++ ++ ++ True ++ vertical ++ ++ ++ True ++ ++ ++ True ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ True ++ True ++ True ++ ++ ++ 1 ++ ++ ++ ++ ++ True ++ True ++ 0 ++ ++ ++ ++ ++ True ++ ++ ++ True ++ ++ ++ 0 ++ ++ ++ ++ ++ True ++ True ++ 1 ++ ++ ++ ++ +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 ++ * ++ */ ++ ++#include "gdm-password-extension.h" ++ ++#include ++#include ++ ++GdmGreeterExtension * ++gdm_greeter_plugin_get_extension (void) ++{ ++ static GObject *extension; ++ ++ if (extension != NULL) { ++ g_object_ref (extension); ++ } else { ++ extension = g_object_new (GDM_TYPE_PASSWORD_EXTENSION, NULL); ++ g_object_add_weak_pointer (extension, (gpointer *) &extension); ++ } ++ ++ return GDM_GREETER_EXTENSION (extension); ++} +-- +1.6.2.2 + + +From 84507da713c1ed0aa1b49aaff32ef8a3b623a626 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sat, 7 Feb 2009 21:17:49 -0500 +Subject: [PATCH 17/33] 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 2e9a1a0..92a4c85 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -810,15 +810,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) +@@ -864,6 +870,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.6.2.2 + + +From 9449c8b47a1b8a1657599820256f3757d189e890 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 18 Feb 2009 12:32:39 -0500 +Subject: [PATCH 18/33] 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 92a4c85..8711992 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -2256,6 +2256,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) +@@ -2393,6 +2421,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.6.2.2 + + +From c6d4f75ae3d2a9bc6242ea0245989e41becb80c3 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Feb 2009 14:05:20 -0500 +Subject: [PATCH 19/33] Add new api to ask when chooser widget is done loading items + +--- + gui/simple-greeter/gdm-chooser-widget.c | 9 +++++++++ + gui/simple-greeter/gdm-chooser-widget.h | 2 ++ + 2 files changed, 11 insertions(+), 0 deletions(-) + +diff --git a/gui/simple-greeter/gdm-chooser-widget.c b/gui/simple-greeter/gdm-chooser-widget.c +index b1ba9b4..a7dbba5 100644 +--- a/gui/simple-greeter/gdm-chooser-widget.c ++++ b/gui/simple-greeter/gdm-chooser-widget.c +@@ -93,6 +93,7 @@ struct GdmChooserWidgetPrivate + guint32 should_hide_inactive_items : 1; + guint32 emit_activated_after_resize_animation : 1; + guint32 was_fully_grown : 1; ++ guint32 is_loaded : 1; + + GdmChooserWidgetPosition separator_position; + GdmChooserWidgetState state; +@@ -2502,8 +2503,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) + { ++ 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.6.2.2 + + +From 30a9844e1ce4db5363d2179bd8d6ad363fa44c71 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 20 Feb 2009 14:31:27 -0500 +Subject: [PATCH 20/33] 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 8711992..e44122c 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -825,6 +825,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) +@@ -839,7 +860,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.6.2.2 + + +From 53437dbdb93925167dd8ae084a0ba97af8b03cff Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 6 Feb 2009 16:25:47 -0500 +Subject: [PATCH 21/33] Add fingerprint plugin + +This commit adds a plugin to initiate a conversation for +fingerprint scans. +--- + configure.ac | 1 + + gui/simple-greeter/plugins/Makefile.am | 2 +- + gui/simple-greeter/plugins/fingerprint/Makefile.am | 54 ++++ + .../fingerprint/gdm-fingerprint-extension.c | 299 ++++++++++++++++++++ + .../fingerprint/gdm-fingerprint-extension.h | 56 ++++ + .../plugins/fingerprint/gdm-fingerprint.pam | 17 ++ + gui/simple-greeter/plugins/fingerprint/page.ui | 56 ++++ + gui/simple-greeter/plugins/fingerprint/plugin.c | 40 +++ + 8 files changed, 524 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/page.ui + create mode 100644 gui/simple-greeter/plugins/fingerprint/plugin.c + +diff --git a/configure.ac b/configure.ac +index f0842c8..13b2cac 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1402,6 +1402,7 @@ 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-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..d5f7e28 +--- /dev/null ++++ b/gui/simple-greeter/plugins/fingerprint/Makefile.am +@@ -0,0 +1,54 @@ ++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..7f5eb1c +--- /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 ++ * ++ */ ++ ++#include ++#include "gdm-fingerprint-extension.h" ++#include "gdm-conversation.h" ++#include "gdm-task.h" ++ ++#include ++#include ++#include ++ ++struct _GdmFingerprintExtensionPrivate ++{ ++ GIcon *icon; ++ GtkWidget *page; ++ GtkActionGroup *actions; ++ ++ GtkWidget *message_label; ++ GtkWidget *prompt_label; ++ GtkWidget *prompt_entry; ++ ++ guint answer_pending : 1; ++}; ++ ++static void gdm_fingerprint_extension_finalize (GObject *object); ++ ++static void gdm_task_iface_init (GdmTaskIface *iface); ++static void gdm_conversation_iface_init (GdmConversationIface *iface); ++static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface); ++ ++G_DEFINE_TYPE_WITH_CODE (GdmFingerprintExtension, ++ gdm_fingerprint_extension, ++ G_TYPE_OBJECT, ++ G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION, ++ gdm_greeter_extension_iface_init) ++ G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK, ++ gdm_task_iface_init) ++ G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION, ++ gdm_conversation_iface_init)); ++ ++static void ++gdm_fingerprint_extension_set_message (GdmConversation *conversation, ++ const char *message) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ gtk_widget_show (extension->priv->message_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message); ++} ++ ++static void ++gdm_fingerprint_extension_ask_question (GdmConversation *conversation, ++ const char *message) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ gtk_widget_show (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++ gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE); ++ gtk_widget_show (extension->priv->prompt_entry); ++ gtk_widget_grab_focus (extension->priv->prompt_entry); ++ extension->priv->answer_pending = TRUE; ++} ++ ++static void ++gdm_fingerprint_extension_ask_secret (GdmConversation *conversation, ++ const char *message) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ gtk_widget_show (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message); ++ gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++ gtk_widget_show (extension->priv->prompt_entry); ++ gtk_widget_grab_focus (extension->priv->prompt_entry); ++ extension->priv->answer_pending = TRUE; ++} ++ ++static void ++gdm_fingerprint_extension_reset (GdmConversation *conversation) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ gtk_widget_hide (extension->priv->prompt_label); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), ""); ++ ++ gtk_widget_hide (extension->priv->prompt_entry); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++ gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE); ++ extension->priv->answer_pending = FALSE; ++ ++ gdm_task_set_enabled (GDM_TASK (conversation), FALSE); ++} ++ ++static void ++gdm_fingerprint_extension_set_ready (GdmConversation *conversation) ++{ ++ gdm_task_set_enabled (GDM_TASK (conversation), TRUE); ++} ++ ++char * ++gdm_fingerprint_extension_get_service_name (GdmConversation *conversation) ++{ ++ return g_strdup (PAMSERVICENAME); ++} ++ ++GtkWidget * ++gdm_fingerprint_extension_get_page (GdmConversation *conversation) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ return extension->priv->page; ++} ++ ++GtkActionGroup * ++gdm_fingerprint_extension_get_actions (GdmConversation *conversation) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ ++ return g_object_ref (extension->priv->actions); ++} ++ ++void ++gdm_fingerprint_extension_request_answer (GdmConversation *conversation) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ const char *text; ++ ++ if (!extension->priv->answer_pending) { ++ gdm_conversation_answer (conversation, NULL); ++ return; ++ } ++ ++ extension->priv->answer_pending = FALSE; ++ text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry)); ++ gdm_conversation_answer (conversation, text); ++ ++ gtk_widget_hide (extension->priv->prompt_entry); ++ gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), ""); ++ gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); ++} ++ ++gboolean ++gdm_fingerprint_extension_focus (GdmConversation *conversation) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation); ++ ++ if (!extension->priv->answer_pending) { ++ return FALSE; ++ } ++ ++ gtk_widget_grab_focus (extension->priv->prompt_entry); ++ return TRUE; ++} ++ ++GIcon * ++gdm_fingerprint_extension_get_icon (GdmTask *task) ++{ ++ GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (task); ++ return g_object_ref (extension->priv->icon); ++} ++ ++char * ++gdm_fingerprint_extension_get_name (GdmTask *task) ++{ ++ return g_strdup (_("Fingerprint Authentication")); ++} ++ ++char * ++gdm_fingerprint_extension_get_description (GdmTask *task) ++{ ++ return g_strdup (_("Log into session with fingerprint")); ++} ++ ++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 ("stock_allow-effects"); ++ 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 ++ */ ++ ++#ifndef __GDM_FINGERPRINT_EXTENSION_H ++#define __GDM_FINGERPRINT_EXTENSION_H ++ ++#include ++#include "gdm-greeter-extension.h" ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_FINGERPRINT_EXTENSION (gdm_fingerprint_extension_get_type ()) ++#define GDM_FINGERPRINT_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtension)) ++#define GDM_FINGERPRINT_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtensionClass)) ++#define GDM_IS_FINGERPRINT_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_FINGERPRINT_EXTENSION)) ++#define GDM_IS_FINGERPRINT_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_FINGERPRINT_EXTENSION)) ++#define GDM_FINGERPRINT_EXTENSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtensionClass)) ++ ++typedef struct _GdmFingerprintExtensionPrivate GdmFingerprintExtensionPrivate; ++ ++typedef struct ++{ ++ GObject parent; ++ GdmFingerprintExtensionPrivate *priv; ++} GdmFingerprintExtension; ++ ++typedef struct ++{ ++ GObjectClass parent_class; ++} GdmFingerprintExtensionClass; ++ ++GType gdm_fingerprint_extension_get_type (void); ++ ++GdmFingerprintExtension *gdm_fingerprint_extension_new (void); ++ ++G_END_DECLS ++ ++#endif /* GDM_FINGERPRINT_EXTENSION_H */ +diff --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/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 @@ ++ ++ ++ ++ ++ True ++ vertical ++ ++ ++ True ++ ++ ++ True ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ True ++ True ++ True ++ ++ ++ 1 ++ ++ ++ ++ ++ True ++ True ++ 0 ++ ++ ++ ++ ++ True ++ ++ ++ True ++ ++ ++ 0 ++ ++ ++ ++ ++ True ++ True ++ 1 ++ ++ ++ ++ +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 ++ * ++ */ ++ ++#include "gdm-fingerprint-extension.h" ++ ++#include ++#include ++ ++GdmGreeterExtension * ++gdm_greeter_plugin_get_extension (void) ++{ ++ static GObject *extension; ++ ++ if (extension != NULL) { ++ g_object_ref (extension); ++ } else { ++ extension = g_object_new (GDM_TYPE_FINGERPRINT_EXTENSION, NULL); ++ g_object_add_weak_pointer (extension, (gpointer *) &extension); ++ } ++ ++ return GDM_GREETER_EXTENSION (extension); ++} +-- +1.6.2.2 + + +From 49ade5091550014f22b93ef888861f536fb04fd8 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 11 Feb 2009 08:47:52 -0500 +Subject: [PATCH 22/33] 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 | 8 + + gui/simple-greeter/plugins/Makefile.am | 2 +- + gui/simple-greeter/plugins/smartcard/Makefile.am | 75 ++ + .../plugins/smartcard/gdm-smartcard-extension.c | 420 ++++++ + .../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 + + gui/simple-greeter/plugins/smartcard/page.ui | 56 + + gui/simple-greeter/plugins/smartcard/plugin.c | 40 + + 13 files changed, 2973 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/page.ui + create mode 100644 gui/simple-greeter/plugins/smartcard/plugin.c + +diff --git a/configure.ac b/configure.ac +index 13b2cac..af5d316 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -67,6 +67,7 @@ GNOME_PANEL_REQUIRED_VERSION=2.0.0 + LIBXKLAVIER_REQUIRED_VERSION=3.5 + #FONTCONFIG_REQUIRED_VERSION=2.6.0 + FONTCONFIG_REQUIRED_VERSION=2.5.0 ++NSS_REQUIRED_VERSION=3.11.1 + + EXTRA_COMPILE_WARNINGS(yes) + +@@ -89,6 +90,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 +@@ -1403,6 +1410,7 @@ gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc + gui/simple-greeter/plugins/Makefile + gui/simple-greeter/plugins/password/Makefile + gui/simple-greeter/plugins/fingerprint/Makefile ++gui/simple-greeter/plugins/smartcard/Makefile + gui/simple-chooser/Makefile + gui/user-switch-applet/Makefile + utils/Makefile +diff --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..15d0174 +--- /dev/null ++++ b/gui/simple-greeter/plugins/smartcard/Makefile.am +@@ -0,0 +1,75 @@ ++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..621d1ab +--- /dev/null ++++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c +@@ -0,0 +1,420 @@ ++/* ++ * Copyright (C) 2009 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Written By: Ray Strode ++ * ++ */ ++ ++#include ++#include "gdm-smartcard-extension.h" ++#include "gdm-conversation.h" ++#include "gdm-task.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifndef GDM_SMARTCARD_WORKER_COMMAND ++#define GDM_SMARTCARD_WORKER_COMMAND LIBEXECDIR "/gdm-smartcard-worker" ++#endif ++ ++struct _GdmSmartcardExtensionPrivate ++{ ++ GIcon *icon; ++ GtkWidget *page; ++ GtkActionGroup *actions; ++ GtkAction *login_action; ++ ++ GtkWidget *message_label; ++ GtkWidget *prompt_label; ++ GtkWidget *prompt_entry; ++ ++ GPid worker_pid; ++ int number_of_tokens; ++ ++ guint answer_pending : 1; ++}; ++ ++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"), ++ _("Log into the currently selected sesson"), ++ NULL); ++ g_signal_connect_swapped (action, "activate", ++ G_CALLBACK (on_activate_log_in), extension); ++ g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL); ++ gtk_action_group_add_action (extension->priv->actions, ++ action); ++ ++ gtk_action_set_visible (action, FALSE); ++ extension->priv->login_action = action; ++} ++ ++static void ++gdm_smartcard_extension_init (GdmSmartcardExtension *extension) ++{ ++ extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension, ++ GDM_TYPE_SMARTCARD_EXTENSION, ++ GdmSmartcardExtensionPrivate); ++ ++ extension->priv->icon = g_themed_icon_new ("apple-green"); ++ create_page (extension); ++ create_actions (extension); ++ gdm_smartcard_extension_reset (GDM_CONVERSATION (extension)); ++} +diff --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 ++ */ ++ ++#ifndef __GDM_SMARTCARD_EXTENSION_H ++#define __GDM_SMARTCARD_EXTENSION_H ++ ++#include ++#include "gdm-greeter-extension.h" ++ ++G_BEGIN_DECLS ++ ++#define GDM_TYPE_SMARTCARD_EXTENSION (gdm_smartcard_extension_get_type ()) ++#define GDM_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtension)) ++#define GDM_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass)) ++#define GDM_IS_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD_EXTENSION)) ++#define GDM_IS_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD_EXTENSION)) ++#define GDM_SMARTCARD_EXTENSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass)) ++ ++typedef struct _GdmSmartcardExtensionPrivate GdmSmartcardExtensionPrivate; ++ ++typedef struct ++{ ++ GObject parent; ++ GdmSmartcardExtensionPrivate *priv; ++} GdmSmartcardExtension; ++ ++typedef struct ++{ ++ GObjectClass parent_class; ++} GdmSmartcardExtensionClass; ++ ++GType gdm_smartcard_extension_get_type (void); ++ ++GdmSmartcardExtension *gdm_smartcard_extension_new (void); ++ ++G_END_DECLS ++ ++#endif /* GDM_SMARTCARD_EXTENSION_H */ +diff --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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef GDM_SMARTCARD_MANAGER_DRIVER ++#define GDM_SMARTCARD_MANAGER_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so" ++#endif ++ ++#ifndef GDM_SMARTCARD_MANAGER_NSS_DB ++#define GDM_SMARTCARD_MANAGER_NSS_DB SYSCONFDIR"/pki/nssdb" ++#endif ++ ++#ifndef GDM_MAX_OPEN_FILE_DESCRIPTORS ++#define GDM_MAX_OPEN_FILE_DESCRIPTORS 1024 ++#endif ++ ++#ifndef GDM_OPEN_FILE_DESCRIPTORS_DIR ++#define GDM_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd" ++#endif ++ ++typedef enum _GdmSmartcardManagerState GdmSmartcardManagerState; ++typedef struct _GdmSmartcardManagerWorker GdmSmartcardManagerWorker; ++ ++enum _GdmSmartcardManagerState { ++ GDM_SMARTCARD_MANAGER_STATE_STOPPED = 0, ++ GDM_SMARTCARD_MANAGER_STATE_STARTING, ++ GDM_SMARTCARD_MANAGER_STATE_STARTED, ++ GDM_SMARTCARD_MANAGER_STATE_STOPPING, ++}; ++ ++struct _GdmSmartcardManagerPrivate { ++ GdmSmartcardManagerState state; ++ SECMODModule *module; ++ char *module_path; ++ ++ GSource *smartcard_event_source; ++ GPid smartcard_event_watcher_pid; ++ GHashTable *smartcards; ++ ++ GThread *worker_thread; ++ ++ guint poll_timeout_id; ++ ++ guint32 is_unstoppable : 1; ++ guint32 nss_is_loaded : 1; ++}; ++ ++struct _GdmSmartcardManagerWorker { ++ SECMODModule *module; ++ GHashTable *smartcards; ++ gint write_fd; ++ ++ guint32 nss_is_loaded : 1; ++}; ++ ++static void gdm_smartcard_manager_finalize (GObject *object); ++static void gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *service_class); ++static void gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *service_class); ++static void gdm_smartcard_manager_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec); ++static void gdm_smartcard_manager_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec); ++static void gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager, ++ const char *module_path); ++static void gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager, ++ GdmSmartcard *card); ++static void gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager_class, ++ GdmSmartcard *card); ++static gboolean gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager); ++static void gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager); ++ ++static gboolean gdm_smartcard_manager_create_worker (GdmSmartcardManager *manager, ++ int *worker_fd, GThread **worker_thread); ++ ++static GdmSmartcardManagerWorker * gdm_smartcard_manager_worker_new (gint write_fd); ++static void gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker); ++static gboolean gdm_open_pipe (gint *write_fd, gint *read_fd); ++static gboolean sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes); ++static gboolean sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes); ++static GdmSmartcard *sc_read_smartcard (gint fd, SECMODModule *module); ++static gboolean sc_write_smartcard (gint fd, GdmSmartcard *card); ++ ++enum { ++ PROP_0 = 0, ++ PROP_MODULE_PATH, ++ NUMBER_OF_PROPERTIES ++}; ++ ++enum { ++ SMARTCARD_INSERTED = 0, ++ SMARTCARD_REMOVED, ++ ERROR, ++ NUMBER_OF_SIGNALS ++}; ++ ++static guint gdm_smartcard_manager_signals[NUMBER_OF_SIGNALS]; ++ ++G_DEFINE_TYPE (GdmSmartcardManager, ++ gdm_smartcard_manager, ++ G_TYPE_OBJECT); ++ ++static void ++gdm_smartcard_manager_class_init (GdmSmartcardManagerClass *manager_class) ++{ ++ GObjectClass *gobject_class; ++ ++ gobject_class = G_OBJECT_CLASS (manager_class); ++ ++ gobject_class->finalize = gdm_smartcard_manager_finalize; ++ ++ gdm_smartcard_manager_class_install_signals (manager_class); ++ gdm_smartcard_manager_class_install_properties (manager_class); ++ ++ g_type_class_add_private (manager_class, ++ sizeof (GdmSmartcardManagerPrivate)); ++} ++ ++static void ++gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *card_class) ++{ ++ GObjectClass *object_class; ++ GParamSpec *param_spec; ++ ++ object_class = G_OBJECT_CLASS (card_class); ++ object_class->set_property = gdm_smartcard_manager_set_property; ++ object_class->get_property = gdm_smartcard_manager_get_property; ++ ++ param_spec = g_param_spec_string ("module-path", _("Module Path"), ++ _("path to smartcard PKCS #11 driver"), ++ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ g_object_class_install_property (object_class, PROP_MODULE_PATH, param_spec); ++} ++ ++static void ++gdm_smartcard_manager_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object); ++ ++ switch (prop_id) { ++ case PROP_MODULE_PATH: ++ gdm_smartcard_manager_set_module_path (manager, ++ g_value_get_string (value)); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gdm_smartcard_manager_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object); ++ char *module_path; ++ ++ switch (prop_id) { ++ case PROP_MODULE_PATH: ++ module_path = gdm_smartcard_manager_get_module_path (manager); ++ g_value_set_string (value, module_path); ++ g_free (module_path); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++char * ++gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager) ++{ ++ return manager->priv->module_path; ++} ++ ++static void ++gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager, ++ const char *module_path) ++{ ++ if ((manager->priv->module_path == NULL) && (module_path == NULL)) { ++ return; ++ } ++ ++ if (((manager->priv->module_path == NULL) || ++ (module_path == NULL) || ++ (strcmp (manager->priv->module_path, module_path) != 0))) { ++ g_free (manager->priv->module_path); ++ manager->priv->module_path = g_strdup (module_path); ++ g_object_notify (G_OBJECT (manager), "module-path"); ++ } ++} ++ ++static void ++gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager, ++ GdmSmartcard *card) ++{ ++ g_debug ("informing smartcard of its removal"); ++ _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED); ++ g_debug ("done"); ++} ++ ++static void ++gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager, ++ GdmSmartcard *card) ++{ ++ g_debug ("informing smartcard of its insertion"); ++ ++ _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED); ++ g_debug ("done"); ++ ++} ++ ++static void ++gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *manager_class) ++{ ++ GObjectClass *object_class; ++ ++ object_class = G_OBJECT_CLASS (manager_class); ++ ++ gdm_smartcard_manager_signals[SMARTCARD_INSERTED] = ++ g_signal_new ("smartcard-inserted", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (GdmSmartcardManagerClass, ++ smartcard_inserted), ++ NULL, NULL, g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, G_TYPE_POINTER); ++ manager_class->smartcard_inserted = gdm_smartcard_manager_card_inserted_handler; ++ ++ gdm_smartcard_manager_signals[SMARTCARD_REMOVED] = ++ g_signal_new ("smartcard-removed", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (GdmSmartcardManagerClass, ++ smartcard_removed), ++ NULL, NULL, g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, G_TYPE_POINTER); ++ manager_class->smartcard_removed = gdm_smartcard_manager_card_removed_handler; ++ ++ gdm_smartcard_manager_signals[ERROR] = ++ g_signal_new ("error", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmSmartcardManagerClass, error), ++ NULL, NULL, g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, G_TYPE_POINTER); ++ manager_class->error = NULL; ++} ++ ++static gboolean ++sc_slot_id_equal (CK_SLOT_ID *slot_id_1, ++ CK_SLOT_ID *slot_id_2) ++{ ++ g_assert (slot_id_1 != NULL); ++ g_assert (slot_id_2 != NULL); ++ ++ return *slot_id_1 == *slot_id_2; ++} ++ ++static gboolean ++sc_slot_id_hash (CK_SLOT_ID *slot_id) ++{ ++ guint32 upper_bits, lower_bits; ++ gint temp; ++ ++ if (sizeof (CK_SLOT_ID) == sizeof (gint)) { ++ return g_int_hash (slot_id); ++ } ++ ++ upper_bits = ((*slot_id) >> 31) - 1; ++ lower_bits = (*slot_id) & 0xffffffff; ++ ++ /* The upper bits are almost certainly always zero, ++ * so let's degenerate to g_int_hash for the ++ * (very) common case ++ */ ++ temp = lower_bits + upper_bits; ++ return upper_bits + g_int_hash (&temp); ++} ++ ++static void ++gdm_smartcard_manager_init (GdmSmartcardManager *manager) ++{ ++ g_debug ("initializing smartcard manager"); ++ ++ manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, ++ GDM_TYPE_SMARTCARD_MANAGER, ++ GdmSmartcardManagerPrivate); ++ manager->priv->poll_timeout_id = 0; ++ manager->priv->is_unstoppable = FALSE; ++ manager->priv->module = NULL; ++ ++ manager->priv->smartcards = ++ g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) g_object_unref); ++ ++ if (!g_thread_supported()) { ++ g_thread_init (NULL); ++ } ++ ++} ++ ++static void ++gdm_smartcard_manager_finalize (GObject *object) ++{ ++ GdmSmartcardManager *manager; ++ GObjectClass *gobject_class; ++ ++ manager = GDM_SMARTCARD_MANAGER (object); ++ gobject_class = ++ G_OBJECT_CLASS (gdm_smartcard_manager_parent_class); ++ ++ gdm_smartcard_manager_stop_now (manager); ++ ++ g_hash_table_destroy (manager->priv->smartcards); ++ manager->priv->smartcards = NULL; ++ ++ gobject_class->finalize (object); ++} ++ ++GQuark ++gdm_smartcard_manager_error_quark (void) ++{ ++ static GQuark error_quark = 0; ++ ++ if (error_quark == 0) { ++ error_quark = g_quark_from_static_string ("gdm-smartcard-manager-error-quark"); ++ } ++ ++ return error_quark; ++} ++ ++GdmSmartcardManager * ++gdm_smartcard_manager_new (const char *module_path) ++{ ++ GdmSmartcardManager *instance; ++ ++ instance = GDM_SMARTCARD_MANAGER (g_object_new (GDM_TYPE_SMARTCARD_MANAGER, ++ "module-path", module_path, ++ NULL)); ++ ++ return instance; ++} ++ ++static void ++gdm_smartcard_manager_emit_error (GdmSmartcardManager *manager, ++ GError *error) ++{ ++ manager->priv->is_unstoppable = TRUE; ++ g_signal_emit (manager, gdm_smartcard_manager_signals[ERROR], 0, ++ error); ++ manager->priv->is_unstoppable = FALSE; ++} ++ ++static void ++gdm_smartcard_manager_emit_smartcard_inserted (GdmSmartcardManager *manager, ++ GdmSmartcard *card) ++{ ++ manager->priv->is_unstoppable = TRUE; ++ g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_INSERTED], 0, ++ card); ++ manager->priv->is_unstoppable = FALSE; ++} ++ ++static void ++gdm_smartcard_manager_emit_smartcard_removed (GdmSmartcardManager *manager, ++ GdmSmartcard *card) ++{ ++ GdmSmartcardManagerState old_state; ++ ++ old_state = manager->priv->state; ++ manager->priv->is_unstoppable = TRUE; ++ g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_REMOVED], 0, ++ card); ++ manager->priv->is_unstoppable = FALSE; ++} ++ ++static gboolean ++gdm_smartcard_manager_check_for_and_process_events (GIOChannel *io_channel, ++ GIOCondition condition, ++ GdmSmartcardManager *manager) ++{ ++ GdmSmartcard *card; ++ gboolean should_stop; ++ guchar event_type; ++ char *card_name; ++ gint fd; ++ ++ card = NULL; ++ should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR); ++ ++ if (should_stop) { ++ g_debug ("received %s on event socket, stopping " ++ "manager...", ++ (condition & G_IO_HUP) && (condition & G_IO_ERR)? ++ "error and hangup" : ++ (condition & G_IO_HUP)? ++ "hangup" : "error"); ++ } ++ ++ if (!(condition & G_IO_IN)) { ++ goto out; ++ } ++ ++ fd = g_io_channel_unix_get_fd (io_channel); ++ ++ event_type = '\0'; ++ if (!sc_read_bytes (fd, &event_type, 1)) { ++ should_stop = TRUE; ++ goto out; ++ } ++ ++ card = sc_read_smartcard (fd, manager->priv->module); ++ ++ if (card == NULL) { ++ should_stop = TRUE; ++ goto out; ++ } ++ ++ card_name = gdm_smartcard_get_name (card); ++ ++ switch (event_type) { ++ case 'I': ++ g_hash_table_replace (manager->priv->smartcards, ++ card_name, card); ++ card_name = NULL; ++ ++ gdm_smartcard_manager_emit_smartcard_inserted (manager, card); ++ card = NULL; ++ break; ++ ++ case 'R': ++ gdm_smartcard_manager_emit_smartcard_removed (manager, card); ++ if (!g_hash_table_remove (manager->priv->smartcards, card_name)) { ++ g_debug ("got removal event of unknown card!"); ++ } ++ g_free (card_name); ++ card_name = NULL; ++ card = NULL; ++ break; ++ ++ default: ++ g_free (card_name); ++ card_name = NULL; ++ g_object_unref (card); ++ ++ should_stop = TRUE; ++ break; ++ } ++ ++out: ++ if (should_stop) { ++ GError *error; ++ ++ error = g_error_new (GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, ++ "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source")); ++ ++ gdm_smartcard_manager_emit_error (manager, error); ++ g_error_free (error); ++ gdm_smartcard_manager_stop_now (manager); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void ++gdm_smartcard_manager_event_processing_stopped_handler (GdmSmartcardManager *manager) ++{ ++ manager->priv->smartcard_event_source = NULL; ++ gdm_smartcard_manager_stop_now (manager); ++} ++ ++static gboolean ++gdm_open_pipe (gint *write_fd, ++ gint *read_fd) ++{ ++ gint pipe_fds[2] = { -1, -1 }; ++ ++ g_assert (write_fd != NULL); ++ g_assert (read_fd != NULL); ++ ++ if (pipe (pipe_fds) < 0) { ++ return FALSE; ++ } ++ ++ if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) { ++ close (pipe_fds[0]); ++ close (pipe_fds[1]); ++ return FALSE; ++ } ++ ++ if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) { ++ close (pipe_fds[0]); ++ close (pipe_fds[1]); ++ return FALSE; ++ } ++ ++ *read_fd = pipe_fds[0]; ++ *write_fd = pipe_fds[1]; ++ ++ return TRUE; ++} ++ ++static void ++gdm_smartcard_manager_stop_watching_for_events (GdmSmartcardManager *manager) ++{ ++ if (manager->priv->smartcard_event_source != NULL) { ++ g_source_destroy (manager->priv->smartcard_event_source); ++ manager->priv->smartcard_event_source = NULL; ++ } ++ ++ if (manager->priv->worker_thread != NULL) { ++ SECMOD_CancelWait (manager->priv->module); ++ /* FIXME: The function above doesn't seem to ++ * always wake up WaitForAnyTokenEvent ++ */ ++ exit (0); ++ g_thread_join (manager->priv->worker_thread); ++ manager->priv->worker_thread = NULL; ++ } ++} ++ ++static gboolean ++sc_load_nss (GError **error) ++{ ++ SECStatus status = SECSuccess; ++ static const guint32 flags = ++ NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | ++ NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT | ++ NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD; ++ ++ g_debug ("attempting to load NSS database '%s'", ++ GDM_SMARTCARD_MANAGER_NSS_DB); ++ ++ status = NSS_Initialize (GDM_SMARTCARD_MANAGER_NSS_DB, ++ "", "", SECMOD_DB, flags); ++ ++ if (status != SECSuccess) { ++ gsize error_message_size; ++ char *error_message; ++ ++ error_message_size = PR_GetErrorTextLength (); ++ ++ if (error_message_size == 0) { ++ g_debug ("NSS security system could not be initialized"); ++ g_set_error (error, ++ GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, ++ _("NSS security system could not be initialized")); ++ goto out; ++ } ++ ++ error_message = g_slice_alloc0 (error_message_size); ++ PR_GetErrorText (error_message); ++ ++ g_set_error (error, ++ GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, ++ "%s", error_message); ++ g_debug ("NSS security system could not be initialized - %s", ++ error_message); ++ ++ g_slice_free1 (error_message_size, error_message); ++ ++ goto out; ++ } ++ ++ g_debug ("NSS database sucessfully loaded"); ++ return TRUE; ++ ++out: ++ g_debug ("NSS database couldn't be sucessfully loaded"); ++ return FALSE; ++} ++ ++static SECMODModule * ++load_driver (char *module_path, ++ GError **error) ++{ ++ SECMODModule *module; ++ char *module_spec; ++ gboolean module_explicitly_specified; ++ ++ g_debug ("attempting to load driver..."); ++ ++ module = NULL; ++ module_explicitly_specified = module_path != NULL; ++ if (module_explicitly_specified) { ++ module_spec = g_strdup_printf ("library=\"%s\"", module_path); ++ g_debug ("loading smartcard driver using spec '%s'", ++ module_spec); ++ ++ module = SECMOD_LoadUserModule (module_spec, ++ NULL /* parent */, ++ FALSE /* recurse */); ++ g_free (module_spec); ++ module_spec = NULL; ++ ++ } else { ++ SECMODModuleList *modules, *tmp; ++ ++ modules = SECMOD_GetDefaultModuleList (); ++ ++ for (tmp = modules; tmp != NULL; tmp = tmp->next) { ++ if (!SECMOD_HasRemovableSlots (tmp->module) || ++ !tmp->module->loaded) ++ continue; ++ ++ module = SECMOD_ReferenceModule (tmp->module); ++ break; ++ } ++ ++ /* fallback to compiled in driver path ++ */ ++ if (module == NULL) { ++ module_path = GDM_SMARTCARD_MANAGER_DRIVER; ++ module_spec = g_strdup_printf ("library=\"%s\"", module_path); ++ g_debug ("loading smartcard driver using spec '%s'", ++ module_spec); ++ ++ module = SECMOD_LoadUserModule (module_spec, ++ NULL /* parent */, ++ FALSE /* recurse */); ++ g_free (module_spec); ++ module_spec = NULL; ++ } ++ ++ } ++ ++ if (!module_explicitly_specified && module == NULL) { ++ g_set_error (error, ++ GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, ++ _("no suitable smartcard driver could be found")); ++ } else if (module == NULL || !module->loaded) { ++ ++ gsize error_message_size; ++ char *error_message; ++ ++ if (module != NULL && !module->loaded) { ++ g_debug ("module found but not loaded?!"); ++ SECMOD_DestroyModule (module); ++ module = NULL; ++ } ++ ++ error_message_size = PR_GetErrorTextLength (); ++ ++ if (error_message_size == 0) { ++ g_debug ("smartcard driver '%s' could not be loaded", ++ module_path); ++ g_set_error (error, ++ GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, ++ _("smartcard driver '%s' could not be " ++ "loaded"), module_path); ++ goto out; ++ } ++ ++ error_message = g_slice_alloc0 (error_message_size); ++ PR_GetErrorText (error_message); ++ ++ g_set_error (error, ++ GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, ++ "%s", error_message); ++ ++ g_debug ("smartcard driver '%s' could not be loaded - %s", ++ module_path, error_message); ++ g_slice_free1 (error_message_size, error_message); ++ } ++ ++out: ++ return module; ++} ++ ++static void ++gdm_smartcard_manager_get_all_cards (GdmSmartcardManager *manager) ++{ ++ int i; ++ ++ for (i = 0; i < manager->priv->module->slotCount; i++) { ++ GdmSmartcard *card; ++ CK_SLOT_ID slot_id; ++ gint slot_series; ++ char *card_name; ++ ++ slot_id = PK11_GetSlotID (manager->priv->module->slots[i]); ++ slot_series = PK11_GetSlotSeries (manager->priv->module->slots[i]); ++ ++ card = _gdm_smartcard_new (manager->priv->module, ++ slot_id, slot_series); ++ ++ card_name = gdm_smartcard_get_name (card); ++ ++ g_hash_table_replace (manager->priv->smartcards, ++ card_name, card); ++ } ++} ++ ++gboolean ++gdm_smartcard_manager_start (GdmSmartcardManager *manager, ++ GError **error) ++{ ++ GError *watching_error; ++ gint worker_fd; ++ GPid worker_pid; ++ GIOChannel *io_channel; ++ GSource *source; ++ GIOFlags channel_flags; ++ GError *nss_error; ++ ++ if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED) { ++ g_debug ("smartcard manager already started"); ++ return TRUE; ++ } ++ ++ manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTING; ++ ++ worker_fd = -1; ++ worker_pid = 0; ++ ++ nss_error = NULL; ++ if (!manager->priv->nss_is_loaded && !sc_load_nss (&nss_error)) { ++ g_propagate_error (error, nss_error); ++ goto out; ++ } ++ manager->priv->nss_is_loaded = TRUE; ++ ++ if (manager->priv->module == NULL) { ++ manager->priv->module = load_driver (manager->priv->module_path, &nss_error); ++ } ++ ++ if (manager->priv->module == NULL) { ++ g_propagate_error (error, nss_error); ++ goto out; ++ } ++ ++ if (!gdm_smartcard_manager_create_worker (manager, &worker_fd, &manager->priv->worker_thread)) { ++ ++ g_set_error (error, ++ GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, ++ _("could not watch for incoming card events - %s"), ++ g_strerror (errno)); ++ ++ goto out; ++ } ++ ++ io_channel = g_io_channel_unix_new (worker_fd); ++ ++ channel_flags = g_io_channel_get_flags (io_channel); ++ watching_error = NULL; ++ ++ source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP); ++ g_io_channel_unref (io_channel); ++ io_channel = NULL; ++ ++ manager->priv->smartcard_event_source = source; ++ ++ g_source_set_callback (manager->priv->smartcard_event_source, ++ (GSourceFunc) (GIOFunc) ++ gdm_smartcard_manager_check_for_and_process_events, ++ manager, ++ (GDestroyNotify) ++ gdm_smartcard_manager_event_processing_stopped_handler); ++ g_source_attach (manager->priv->smartcard_event_source, NULL); ++ g_source_unref (manager->priv->smartcard_event_source); ++ ++ /* populate the hash with cards that are already inserted ++ */ ++ gdm_smartcard_manager_get_all_cards (manager); ++ ++ manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTED; ++ ++out: ++ /* don't leave it in a half started state ++ */ ++ if (manager->priv->state != GDM_SMARTCARD_MANAGER_STATE_STARTED) { ++ g_debug ("smartcard manager could not be completely started"); ++ gdm_smartcard_manager_stop (manager); ++ } else { ++ g_debug ("smartcard manager started"); ++ } ++ ++ return manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED; ++} ++ ++static gboolean ++gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager) ++{ ++ if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) { ++ return FALSE; ++ } ++ ++ manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPED; ++ gdm_smartcard_manager_stop_watching_for_events (manager); ++ ++ if (manager->priv->module != NULL) { ++ SECMOD_DestroyModule (manager->priv->module); ++ manager->priv->module = NULL; ++ } ++ ++ if (manager->priv->nss_is_loaded) { ++ NSS_Shutdown (); ++ manager->priv->nss_is_loaded = FALSE; ++ } ++ ++ g_debug ("smartcard manager stopped"); ++ ++ return FALSE; ++} ++ ++static void ++gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager) ++{ ++ ++ manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPING; ++ ++ g_idle_add ((GSourceFunc) gdm_smartcard_manager_stop_now, manager); ++} ++ ++void ++gdm_smartcard_manager_stop (GdmSmartcardManager *manager) ++{ ++ if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) { ++ return; ++ } ++ ++ if (manager->priv->is_unstoppable) { ++ gdm_smartcard_manager_queue_stop (manager); ++ return; ++ } ++ ++ gdm_smartcard_manager_stop_now (manager); ++} ++ ++static void ++gdm_smartcard_manager_check_for_login_card (CK_SLOT_ID slot_id, ++ GdmSmartcard *card, ++ gboolean *is_inserted) ++{ ++ g_assert (is_inserted != NULL); ++ ++ if (gdm_smartcard_is_login_card (card)) { ++ *is_inserted = TRUE; ++ } ++ ++} ++ ++gboolean ++gdm_smartcard_manager_login_card_is_inserted (GdmSmartcardManager *manager) ++ ++{ ++ gboolean is_inserted; ++ ++ is_inserted = FALSE; ++ g_hash_table_foreach (manager->priv->smartcards, ++ (GHFunc) ++ gdm_smartcard_manager_check_for_login_card, ++ &is_inserted); ++ return is_inserted; ++} ++ ++static GdmSmartcardManagerWorker * ++gdm_smartcard_manager_worker_new (gint write_fd) ++{ ++ GdmSmartcardManagerWorker *worker; ++ ++ worker = g_slice_new0 (GdmSmartcardManagerWorker); ++ worker->write_fd = write_fd; ++ worker->module = NULL; ++ ++ worker->smartcards = ++ g_hash_table_new_full ((GHashFunc) sc_slot_id_hash, ++ (GEqualFunc) sc_slot_id_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) g_object_unref); ++ ++ return worker; ++} ++ ++static void ++gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker) ++{ ++ if (worker->smartcards != NULL) { ++ g_hash_table_destroy (worker->smartcards); ++ worker->smartcards = NULL; ++ } ++ ++ g_slice_free (GdmSmartcardManagerWorker, worker); ++} ++ ++static gboolean ++sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes) ++{ ++ size_t bytes_left; ++ size_t total_bytes_read; ++ ssize_t bytes_read; ++ ++ bytes_left = (size_t) num_bytes; ++ total_bytes_read = 0; ++ ++ do { ++ bytes_read = read (fd, bytes + total_bytes_read, bytes_left); ++ g_assert (bytes_read <= (ssize_t) bytes_left); ++ ++ if (bytes_read <= 0) { ++ if ((bytes_read < 0) && (errno == EINTR || errno == EAGAIN)) { ++ continue; ++ } ++ ++ bytes_left = 0; ++ } else { ++ bytes_left -= bytes_read; ++ total_bytes_read += bytes_read; ++ } ++ } while (bytes_left > 0); ++ ++ if (total_bytes_read < (size_t) num_bytes) { ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes) ++{ ++ size_t bytes_left; ++ size_t total_bytes_written; ++ ssize_t bytes_written; ++ ++ bytes_left = (size_t) num_bytes; ++ total_bytes_written = 0; ++ ++ do { ++ bytes_written = write (fd, bytes + total_bytes_written, bytes_left); ++ g_assert (bytes_written <= (ssize_t) bytes_left); ++ ++ if (bytes_written <= 0) { ++ if ((bytes_written < 0) && (errno == EINTR || errno == EAGAIN)) { ++ continue; ++ } ++ ++ bytes_left = 0; ++ } else { ++ bytes_left -= bytes_written; ++ total_bytes_written += bytes_written; ++ } ++ } while (bytes_left > 0); ++ ++ if (total_bytes_written < (size_t) num_bytes) { ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static GdmSmartcard * ++sc_read_smartcard (gint fd, ++ SECMODModule *module) ++{ ++ GdmSmartcard *card; ++ char *card_name; ++ gsize card_name_size; ++ ++ card_name_size = 0; ++ if (!sc_read_bytes (fd, &card_name_size, sizeof (card_name_size))) { ++ return NULL; ++ } ++ ++ card_name = g_slice_alloc0 (card_name_size); ++ if (!sc_read_bytes (fd, card_name, card_name_size)) { ++ g_slice_free1 (card_name_size, card_name); ++ return NULL; ++ } ++ card = _gdm_smartcard_new_from_name (module, card_name); ++ g_slice_free1 (card_name_size, card_name); ++ ++ return card; ++} ++ ++static gboolean ++sc_write_smartcard (gint fd, ++ GdmSmartcard *card) ++{ ++ gsize card_name_size; ++ char *card_name; ++ ++ card_name = gdm_smartcard_get_name (card); ++ card_name_size = strlen (card_name) + 1; ++ ++ if (!sc_write_bytes (fd, &card_name_size, sizeof (card_name_size))) { ++ g_free (card_name); ++ return FALSE; ++ } ++ ++ if (!sc_write_bytes (fd, card_name, card_name_size)) { ++ g_free (card_name); ++ return FALSE; ++ } ++ g_free (card_name); ++ ++ return TRUE; ++} ++ ++static gboolean ++gdm_smartcard_manager_worker_emit_smartcard_removed (GdmSmartcardManagerWorker *worker, ++ GdmSmartcard *card, ++ GError **error) ++{ ++ g_debug ("card '%s' removed!", gdm_smartcard_get_name (card)); ++ ++ if (!sc_write_bytes (worker->write_fd, "R", 1)) { ++ goto error_out; ++ } ++ ++ if (!sc_write_smartcard (worker->write_fd, card)) { ++ goto error_out; ++ } ++ ++ return TRUE; ++ ++error_out: ++ g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS, ++ "%s", g_strerror (errno)); ++ return FALSE; ++} ++ ++static gboolean ++gdm_smartcard_manager_worker_emit_smartcard_inserted (GdmSmartcardManagerWorker *worker, ++ GdmSmartcard *card, ++ GError **error) ++{ ++ GError *write_error; ++ ++ write_error = NULL; ++ g_debug ("card '%s' inserted!", gdm_smartcard_get_name (card)); ++ if (!sc_write_bytes (worker->write_fd, "I", 1)) { ++ goto error_out; ++ } ++ ++ if (!sc_write_smartcard (worker->write_fd, card)) { ++ goto error_out; ++ } ++ ++ return TRUE; ++ ++error_out: ++ g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS, ++ "%s", g_strerror (errno)); ++ return FALSE; ++} ++ ++static gboolean ++gdm_smartcard_manager_worker_watch_for_and_process_event (GdmSmartcardManagerWorker *worker, ++ GError **error) ++{ ++ PK11SlotInfo *slot; ++ CK_SLOT_ID slot_id, *key; ++ gint slot_series, card_slot_series; ++ GdmSmartcard *card; ++ GError *processing_error; ++ ++ g_debug ("waiting for card event"); ++ ++ /* FIXME: we return FALSE quite a bit in this function without cleaning up ++ * resources. By returning FALSE we're going to ultimately exit anyway, but ++ * we should still be tidier about things. ++ */ ++ ++ slot = SECMOD_WaitForAnyTokenEvent (worker->module, 0, PR_SecondsToInterval (1)); ++ processing_error = NULL; ++ ++ if (slot == NULL) { ++ int error_code; ++ ++ error_code = PORT_GetError (); ++ if ((error_code == 0) || (error_code == SEC_ERROR_NO_EVENT)) { ++ g_debug ("spurrious event occurred"); ++ return TRUE; ++ } ++ ++ /* FIXME: is there a function to convert from a PORT error ++ * code to a translated string? ++ */ ++ g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR, ++ GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, ++ _("encountered unexpected error while " ++ "waiting for smartcard events")); ++ return FALSE; ++ } ++ ++ /* the slot id and series together uniquely identify a card. ++ * You can never have two cards with the same slot id at the ++ * same time, however (I think), so we can key off of it. ++ */ ++ slot_id = PK11_GetSlotID (slot); ++ slot_series = PK11_GetSlotSeries (slot); ++ ++ /* First check to see if there is a card that we're currently ++ * tracking in the slot. ++ */ ++ key = g_new (CK_SLOT_ID, 1); ++ *key = slot_id; ++ card = g_hash_table_lookup (worker->smartcards, key); ++ ++ if (card != NULL) { ++ card_slot_series = gdm_smartcard_get_slot_series (card); ++ } else { ++ card_slot_series = -1; ++ } ++ ++ if (PK11_IsPresent (slot)) { ++ /* Now, check to see if their is a new card in the slot. ++ * If there was a different card in the slot now than ++ * there was before, then we need to emit a removed signal ++ * for the old card (we don't want unpaired insertion events). ++ */ ++ if ((card != NULL) && ++ card_slot_series != slot_series) { ++ if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { ++ g_propagate_error (error, processing_error); ++ return FALSE; ++ } ++ } ++ ++ card = _gdm_smartcard_new (worker->module, ++ slot_id, slot_series); ++ ++ g_hash_table_replace (worker->smartcards, ++ key, card); ++ key = NULL; ++ ++ if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) { ++ g_propagate_error (error, processing_error); ++ return FALSE; ++ } ++ } else { ++ /* if we aren't tracking the card, just discard the event. ++ * We don't want unpaired remove events. Note on startup ++ * NSS will generate an "insertion" event if a card is ++ * already inserted in the slot. ++ */ ++ if ((card != NULL)) { ++ /* FIXME: i'm not sure about this code. Maybe we ++ * shouldn't do this at all, or maybe we should do it ++ * n times (where n = slot_series - card_slot_series + 1) ++ * ++ * Right now, i'm just doing it once. ++ */ ++ if ((slot_series - card_slot_series) > 1) { ++ ++ if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { ++ g_propagate_error (error, processing_error); ++ return FALSE; ++ } ++ g_hash_table_remove (worker->smartcards, key); ++ ++ card = _gdm_smartcard_new (worker->module, ++ slot_id, slot_series); ++ g_hash_table_replace (worker->smartcards, ++ key, card); ++ key = NULL; ++ if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) { ++ g_propagate_error (error, processing_error); ++ return FALSE; ++ } ++ } ++ ++ if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { ++ g_propagate_error (error, processing_error); ++ return FALSE; ++ } ++ ++ g_hash_table_remove (worker->smartcards, key); ++ card = NULL; ++ } else { ++ g_debug ("got spurious remove event"); ++ } ++ } ++ ++ g_free (key); ++ PK11_FreeSlot (slot); ++ ++ return TRUE; ++} ++ ++static void ++gdm_smartcard_manager_worker_run (GdmSmartcardManagerWorker *worker) ++{ ++ GError *error; ++ ++ ++ error = NULL; ++ ++ while (gdm_smartcard_manager_worker_watch_for_and_process_event (worker, &error)); ++ ++ if (error != NULL) { ++ g_debug ("could not process card event - %s", error->message); ++ g_error_free (error); ++ } ++ ++ gdm_smartcard_manager_worker_free (worker); ++} ++ ++static gboolean ++gdm_smartcard_manager_create_worker (GdmSmartcardManager *manager, ++ gint *worker_fd, ++ GThread **worker_thread) ++{ ++ GdmSmartcardManagerWorker *worker; ++ gint write_fd, read_fd; ++ ++ write_fd = -1; ++ read_fd = -1; ++ if (!gdm_open_pipe (&write_fd, &read_fd)) { ++ return FALSE; ++ } ++ ++ worker = gdm_smartcard_manager_worker_new (write_fd); ++ worker->module = manager->priv->module; ++ ++ *worker_thread = g_thread_create ((GThreadFunc) ++ gdm_smartcard_manager_worker_run, ++ worker, TRUE, NULL); ++ ++ if (*worker_thread == NULL) { ++ gdm_smartcard_manager_worker_free (worker); ++ return FALSE; ++ } ++ ++ if (worker_fd) { ++ *worker_fd = read_fd; ++ } ++ ++ return TRUE; ++} ++ ++#ifdef GDM_SMARTCARD_MANAGER_ENABLE_TEST ++#include ++ ++static GMainLoop *event_loop; ++static gboolean should_exit_on_next_remove = FALSE; ++ ++static gboolean ++on_timeout (GdmSmartcardManager *manager) ++{ ++ GError *error; ++ g_print ("Re-enabling manager.\n"); ++ ++ if (!gdm_smartcard_manager_start (manager, &error)) { ++ g_warning ("could not start smartcard manager - %s", ++ error->message); ++ g_error_free (error); ++ return 1; ++ } ++ g_print ("Please re-insert smartcard\n"); ++ ++ should_exit_on_next_remove = TRUE; ++ ++ return FALSE; ++} ++ ++static void ++on_device_inserted (GdmSmartcardManager *manager, ++ GdmSmartcard *card) ++{ ++ g_print ("smartcard inserted!\n"); ++ g_print ("Please remove it.\n"); ++} ++ ++static void ++on_device_removed (GdmSmartcardManager *manager, ++ GdmSmartcard *card) ++{ ++ g_print ("smartcard removed!\n"); ++ ++ if (should_exit_on_next_remove) { ++ g_main_loop_quit (event_loop); ++ } else { ++ g_print ("disabling manager for 2 seconds\n"); ++ gdm_smartcard_manager_stop (manager); ++ g_timeout_add (2000, (GSourceFunc) on_timeout, manager); ++ } ++} ++ ++int ++main (int argc, ++ char *argv[]) ++{ ++ GdmSmartcardManager *manager; ++ GError *error; ++ ++ g_log_set_always_fatal (G_LOG_LEVEL_ERROR ++ | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); ++ ++ g_type_init (); ++ ++ g_message ("creating instance of 'smartcard manager' object..."); ++ manager = gdm_smartcard_manager_new (NULL); ++ g_message ("'smartcard manager' object created successfully"); ++ ++ g_signal_connect (manager, "smartcard-inserted", ++ G_CALLBACK (on_device_inserted), NULL); ++ ++ g_signal_connect (manager, "smartcard-removed", ++ G_CALLBACK (on_device_removed), NULL); ++ ++ g_message ("starting listener..."); ++ ++ error = NULL; ++ if (!gdm_smartcard_manager_start (manager, &error)) { ++ g_warning ("could not start smartcard manager - %s", ++ error->message); ++ g_error_free (error); ++ return 1; ++ } ++ ++ event_loop = g_main_loop_new (NULL, FALSE); ++ g_main_loop_run (event_loop); ++ g_main_loop_unref (event_loop); ++ event_loop = NULL; ++ ++ g_message ("destroying previously created 'smartcard manager' object..."); ++ g_object_unref (manager); ++ manager = NULL; ++ g_message ("'smartcard manager' object destroyed successfully"); ++ ++ return 0; ++} ++#endif +diff --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 ++#include ++ ++G_BEGIN_DECLS ++#define GDM_TYPE_SMARTCARD_MANAGER (gdm_smartcard_manager_get_type ()) ++#define GDM_SMARTCARD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManager)) ++#define GDM_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass)) ++#define GDM_IS_SMARTCARD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SMARTCARD_MANAGER)) ++#define GDM_IS_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SMARTCARD_MANAGER)) ++#define GDM_SMARTCARD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass)) ++#define GDM_SMARTCARD_MANAGER_ERROR (gdm_smartcard_manager_error_quark ()) ++typedef struct _GdmSmartcardManager GdmSmartcardManager; ++typedef struct _GdmSmartcardManagerClass GdmSmartcardManagerClass; ++typedef struct _GdmSmartcardManagerPrivate GdmSmartcardManagerPrivate; ++typedef enum _GdmSmartcardManagerError GdmSmartcardManagerError; ++ ++struct _GdmSmartcardManager { ++ GObject parent; ++ ++ /*< private > */ ++ GdmSmartcardManagerPrivate *priv; ++}; ++ ++struct _GdmSmartcardManagerClass { ++ GObjectClass parent_class; ++ ++ /* Signals */ ++ void (*smartcard_inserted) (GdmSmartcardManager *manager, ++ GdmSmartcard *token); ++ void (*smartcard_removed) (GdmSmartcardManager *manager, ++ GdmSmartcard *token); ++ void (*error) (GdmSmartcardManager *manager, ++ GError *error); ++}; ++ ++enum _GdmSmartcardManagerError { ++ GDM_SMARTCARD_MANAGER_ERROR_GENERIC = 0, ++ GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, ++ GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, ++ GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, ++ GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS ++}; ++ ++GType gdm_smartcard_manager_get_type (void) G_GNUC_CONST; ++GQuark gdm_smartcard_manager_error_quark (void) G_GNUC_CONST; ++ ++GdmSmartcardManager *gdm_smartcard_manager_new (const char *module); ++ ++gboolean gdm_smartcard_manager_start (GdmSmartcardManager *manager, ++ GError **error); ++ ++void gdm_smartcard_manager_stop (GdmSmartcardManager *manager); ++ ++char *gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager); ++gboolean gdm_smartcard_manager_login_token_is_inserted (GdmSmartcardManager *manager); ++ ++G_END_DECLS ++#endif /* GDM_SMARTCARD_MANAGER_H */ +diff --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 ++#include ++#include ++#include ++ ++#include ++ ++#include "gdm-smartcard-manager.h" ++#include "gdm-smartcard.h" ++ ++#ifndef GDM_SMARTCARDS_CONF ++#define GDM_SMARTCARDS_CONF GDMCONFDIR "/smartcards.conf" ++#endif ++ ++#ifndef GDM_SMARTCARDS_GROUP ++#define GDM_SMARTCARDS_GROUP "Smartcards" ++#endif ++ ++#ifndef GDM_SMARTCARDS_KEY_ENABLED ++#define GDM_SMARTCARDS_KEY_ENABLED "Enabled" ++#endif ++ ++#ifndef GDM_SMARTCARDS_KEY_DRIVER ++#define GDM_SMARTCARDS_KEY_DRIVER "Driver" ++#endif ++ ++static GMainLoop *event_loop; ++static GdmSmartcardManager *manager; ++static int signal_pipe_fds[2] = { -1, -1 }; ++ ++static void ++on_smartcard_event (const char *event_string) ++{ ++ g_debug ("smartcard event '%s' happened", event_string); ++ g_print ("%s", event_string); ++ fflush (stdout); ++} ++ ++static void ++watch_for_smartcards (void) ++{ ++ GError *error; ++ char *driver; ++ GKeyFile *cfg; ++ ++ cfg = g_key_file_new (); ++ ++ error = NULL; ++ driver = NULL; ++ if (g_key_file_load_from_file (cfg, GDM_SMARTCARDS_CONF, G_KEY_FILE_NONE, &error)) { ++ if (!g_key_file_get_boolean (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_ENABLED, &error)) { ++ g_debug ("smartcard support is not enabled"); ++ goto out; ++ } ++ ++ driver = g_key_file_get_string (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_DRIVER, NULL); ++ g_debug ("smartcards driver is set to '%s'", ++ driver == NULL || driver[0] == '\0'? "" : driver); ++ } ++ ++ g_debug ("watching for smartcard insertion and removal events"); ++ manager = gdm_smartcard_manager_new (driver); ++ g_free (driver); ++ ++ g_signal_connect_swapped (manager, ++ "smartcard-inserted", ++ G_CALLBACK (on_smartcard_event), ++ "I"); ++ ++ g_signal_connect_swapped (manager, ++ "smartcard-removed", ++ G_CALLBACK (on_smartcard_event), ++ "R"); ++ ++ error = NULL; ++ if (!gdm_smartcard_manager_start (manager, &error)) { ++ g_object_unref (manager); ++ manager = NULL; ++ ++ if (error != NULL) { ++ g_debug ("%s", error->message); ++ g_error_free (error); ++ } else { ++ g_debug ("could not start smartcard manager"); ++ ++ } ++ goto out; ++ } ++out: ++ g_key_file_free (cfg); ++} ++ ++static void ++stop_watching_for_smartcards (void) ++{ ++ if (manager != NULL) { ++ gdm_smartcard_manager_stop (manager); ++ g_object_unref (manager); ++ manager = NULL; ++ } ++} ++ ++static void ++on_term_signal (int signal_number) ++{ ++ close (signal_pipe_fds[1]); ++ signal_pipe_fds[1] = -1; ++} ++ ++static gboolean ++after_term_signal (GIOChannel *io_channel, ++ GIOCondition condition, ++ gpointer data) ++{ ++ g_main_loop_quit (event_loop); ++ return FALSE; ++} ++ ++static void ++on_debug_message (const char *log_domain, ++ GLogLevelFlags log_level, ++ const char *message, ++ gpointer user_data) ++{ ++ g_printerr ("*** DEBUG: %s\n", message); ++} ++ ++int ++main (int argc, ++ char **argv) ++{ ++ GIOChannel *io_channel; ++ ++ setlocale (LC_ALL, ""); ++ ++ g_type_init (); ++ ++ g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, on_debug_message, NULL); ++ ++ event_loop = g_main_loop_new (NULL, FALSE); ++ ++ watch_for_smartcards (); ++ ++ signal (SIGTERM, on_term_signal); ++ signal (SIGPIPE, on_term_signal); ++ if (pipe (signal_pipe_fds) != 0) { ++ return 1; ++ } ++ fcntl (signal_pipe_fds[0], F_SETFD, FD_CLOEXEC); ++ fcntl (signal_pipe_fds[1], F_SETFD, FD_CLOEXEC); ++ ++ io_channel = g_io_channel_unix_new (signal_pipe_fds[0]); ++ g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL); ++ g_io_channel_set_encoding (io_channel, NULL, NULL); ++ g_io_channel_set_buffered (io_channel, FALSE); ++ g_io_add_watch (io_channel, G_IO_HUP, after_term_signal, NULL); ++ g_io_channel_set_close_on_unref (io_channel, TRUE); ++ g_io_channel_unref (io_channel); ++ ++ g_main_loop_run (event_loop); ++ ++ stop_watching_for_smartcards (); ++ ++ return 0; ++} +diff --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 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ */ ++#define GDM_SMARTCARD_ENABLE_INTERNAL_API ++#include "gdm-smartcard.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct _GdmSmartcardPrivate { ++ SECMODModule *module; ++ GdmSmartcardState state; ++ ++ CK_SLOT_ID slot_id; ++ int slot_series; ++ ++ PK11SlotInfo *slot; ++ char *name; ++ ++ CERTCertificate *signing_certificate; ++ CERTCertificate *encryption_certificate; ++}; ++ ++static void gdm_smartcard_finalize (GObject *object); ++static void gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class); ++static void gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class); ++static void gdm_smartcard_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec); ++static void gdm_smartcard_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec); ++static void gdm_smartcard_set_name (GdmSmartcard *card, const char *name); ++static void gdm_smartcard_set_slot_id (GdmSmartcard *card, ++ int slot_id); ++static void gdm_smartcard_set_slot_series (GdmSmartcard *card, ++ int slot_series); ++static void gdm_smartcard_set_module (GdmSmartcard *card, ++ SECMODModule *module); ++ ++static PK11SlotInfo *gdm_smartcard_find_slot_from_id (GdmSmartcard *card, ++ int slot_id); ++ ++static PK11SlotInfo *gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card, ++ const char *card_name); ++static gboolean gdm_smartcard_fetch_certificates (GdmSmartcard *card); ++ ++#ifndef GDM_SMARTCARD_DEFAULT_SLOT_ID ++#define GDM_SMARTCARD_DEFAULT_SLOT_ID ((gulong) -1) ++#endif ++ ++#ifndef GDM_SMARTCARD_DEFAULT_SLOT_SERIES ++#define GDM_SMARTCARD_DEFAULT_SLOT_SERIES -1 ++#endif ++ ++enum { ++ PROP_0 = 0, ++ PROP_NAME, ++ PROP_SLOT_ID, ++ PROP_SLOT_SERIES, ++ PROP_MODULE, ++ NUMBER_OF_PROPERTIES ++}; ++ ++enum { ++ INSERTED, ++ REMOVED, ++ NUMBER_OF_SIGNALS ++}; ++ ++static guint gdm_smartcard_signals[NUMBER_OF_SIGNALS]; ++ ++G_DEFINE_TYPE (GdmSmartcard, gdm_smartcard, G_TYPE_OBJECT); ++ ++static void ++gdm_smartcard_class_init (GdmSmartcardClass *card_class) ++{ ++ GObjectClass *gobject_class; ++ ++ gobject_class = G_OBJECT_CLASS (card_class); ++ ++ gobject_class->finalize = gdm_smartcard_finalize; ++ ++ gdm_smartcard_class_install_signals (card_class); ++ gdm_smartcard_class_install_properties (card_class); ++ ++ g_type_class_add_private (card_class, ++ sizeof (GdmSmartcardPrivate)); ++} ++ ++static void ++gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class) ++{ ++ GObjectClass *object_class; ++ ++ object_class = G_OBJECT_CLASS (card_class); ++ ++ gdm_smartcard_signals[INSERTED] = ++ g_signal_new ("inserted", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmSmartcardClass, ++ inserted), ++ NULL, NULL, g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ gdm_smartcard_signals[REMOVED] = ++ g_signal_new ("removed", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GdmSmartcardClass, ++ removed), ++ NULL, NULL, g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++} ++ ++static void ++gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class) ++{ ++ GObjectClass *object_class; ++ GParamSpec *param_spec; ++ ++ object_class = G_OBJECT_CLASS (card_class); ++ object_class->set_property = gdm_smartcard_set_property; ++ object_class->get_property = gdm_smartcard_get_property; ++ ++ param_spec = g_param_spec_ulong ("slot-id", _("Slot ID"), ++ _("The slot the card is in"), ++ 1, G_MAXULONG, ++ GDM_SMARTCARD_DEFAULT_SLOT_ID, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ g_object_class_install_property (object_class, PROP_SLOT_ID, param_spec); ++ ++ param_spec = g_param_spec_int ("slot-series", _("Slot Series"), ++ _("per-slot card identifier"), ++ -1, G_MAXINT, ++ GDM_SMARTCARD_DEFAULT_SLOT_SERIES, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ g_object_class_install_property (object_class, PROP_SLOT_SERIES, param_spec); ++ ++ param_spec = g_param_spec_string ("name", _("name"), ++ _("name"), NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ g_object_class_install_property (object_class, PROP_NAME, param_spec); ++ ++ param_spec = g_param_spec_pointer ("module", _("Module"), ++ _("smartcard driver"), ++ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); ++ g_object_class_install_property (object_class, PROP_MODULE, param_spec); ++} ++ ++static void ++gdm_smartcard_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GdmSmartcard *card = GDM_SMARTCARD (object); ++ ++ switch (prop_id) { ++ case PROP_NAME: ++ gdm_smartcard_set_name (card, g_value_get_string (value)); ++ break; ++ ++ case PROP_SLOT_ID: ++ gdm_smartcard_set_slot_id (card, ++ g_value_get_ulong (value)); ++ break; ++ ++ case PROP_SLOT_SERIES: ++ gdm_smartcard_set_slot_series (card, ++ g_value_get_int (value)); ++ break; ++ ++ case PROP_MODULE: ++ gdm_smartcard_set_module (card, ++ (SECMODModule *) ++ g_value_get_pointer (value)); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++CK_SLOT_ID ++gdm_smartcard_get_slot_id (GdmSmartcard *card) ++{ ++ return card->priv->slot_id; ++} ++ ++GdmSmartcardState ++gdm_smartcard_get_state (GdmSmartcard *card) ++{ ++ return card->priv->state; ++} ++ ++char * ++gdm_smartcard_get_name (GdmSmartcard *card) ++{ ++ return g_strdup (card->priv->name); ++} ++ ++gboolean ++gdm_smartcard_is_login_card (GdmSmartcard *card) ++{ ++ const char *login_card_name; ++ login_card_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME"); ++ ++ if ((login_card_name == NULL) || (card->priv->name == NULL)) { ++ return FALSE; ++ } ++ ++ if (strcmp (card->priv->name, login_card_name) == 0) { ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static void ++gdm_smartcard_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GdmSmartcard *card = GDM_SMARTCARD (object); ++ ++ switch (prop_id) { ++ case PROP_NAME: ++ g_value_take_string (value, ++ gdm_smartcard_get_name (card)); ++ break; ++ ++ case PROP_SLOT_ID: ++ g_value_set_ulong (value, ++ (gulong) gdm_smartcard_get_slot_id (card)); ++ break; ++ ++ case PROP_SLOT_SERIES: ++ g_value_set_int (value, ++ gdm_smartcard_get_slot_series (card)); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++gdm_smartcard_set_name (GdmSmartcard *card, ++ const char *name) ++{ ++ if (name == NULL) { ++ return; ++ } ++ ++ if ((card->priv->name == NULL) || ++ (strcmp (card->priv->name, name) != 0)) { ++ g_free (card->priv->name); ++ card->priv->name = g_strdup (name); ++ ++ if (card->priv->slot == NULL) { ++ card->priv->slot = gdm_smartcard_find_slot_from_card_name (card, ++ card->priv->name); ++ ++ if (card->priv->slot != NULL) { ++ int slot_id, slot_series; ++ ++ slot_id = PK11_GetSlotID (card->priv->slot); ++ if (slot_id != card->priv->slot_id) { ++ gdm_smartcard_set_slot_id (card, slot_id); ++ } ++ ++ slot_series = PK11_GetSlotSeries (card->priv->slot); ++ if (slot_series != card->priv->slot_series) { ++ gdm_smartcard_set_slot_series (card, slot_series); ++ } ++ ++ _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED); ++ } else { ++ _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED); ++ } ++ } ++ ++ g_object_notify (G_OBJECT (card), "name"); ++ } ++} ++ ++static void ++gdm_smartcard_set_slot_id (GdmSmartcard *card, ++ int slot_id) ++{ ++ if (card->priv->slot_id != slot_id) { ++ card->priv->slot_id = slot_id; ++ ++ if (card->priv->slot == NULL) { ++ card->priv->slot = gdm_smartcard_find_slot_from_id (card, ++ card->priv->slot_id); ++ ++ if (card->priv->slot != NULL) { ++ const char *card_name; ++ ++ card_name = PK11_GetTokenName (card->priv->slot); ++ if ((card->priv->name == NULL) || ++ ((card_name != NULL) && ++ (strcmp (card_name, card->priv->name) != 0))) { ++ gdm_smartcard_set_name (card, card_name); ++ } ++ ++ _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED); ++ } else { ++ _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED); ++ } ++ } ++ ++ g_object_notify (G_OBJECT (card), "slot-id"); ++ } ++} ++ ++static void ++gdm_smartcard_set_slot_series (GdmSmartcard *card, ++ int slot_series) ++{ ++ if (card->priv->slot_series != slot_series) { ++ card->priv->slot_series = slot_series; ++ g_object_notify (G_OBJECT (card), "slot-series"); ++ } ++} ++ ++static void ++gdm_smartcard_set_module (GdmSmartcard *card, ++ SECMODModule *module) ++{ ++ gboolean should_notify; ++ ++ if (card->priv->module != module) { ++ should_notify = TRUE; ++ } else { ++ should_notify = FALSE; ++ } ++ ++ if (card->priv->module != NULL) { ++ SECMOD_DestroyModule (card->priv->module); ++ card->priv->module = NULL; ++ } ++ ++ if (module != NULL) { ++ card->priv->module = SECMOD_ReferenceModule (module); ++ } ++ ++ if (should_notify) { ++ g_object_notify (G_OBJECT (card), "module"); ++ } ++} ++ ++int ++gdm_smartcard_get_slot_series (GdmSmartcard *card) ++{ ++ return card->priv->slot_series; ++} ++ ++static void ++gdm_smartcard_init (GdmSmartcard *card) ++{ ++ ++ g_debug ("initializing smartcard "); ++ ++ card->priv = G_TYPE_INSTANCE_GET_PRIVATE (card, ++ GDM_TYPE_SMARTCARD, ++ GdmSmartcardPrivate); ++ ++ if (card->priv->slot != NULL) { ++ card->priv->name = g_strdup (PK11_GetTokenName (card->priv->slot)); ++ } ++} ++ ++static void gdm_smartcard_finalize (GObject *object) ++{ ++ GdmSmartcard *card; ++ GObjectClass *gobject_class; ++ ++ card = GDM_SMARTCARD (object); ++ ++ g_free (card->priv->name); ++ ++ gdm_smartcard_set_module (card, NULL); ++ ++ gobject_class = G_OBJECT_CLASS (gdm_smartcard_parent_class); ++ ++ gobject_class->finalize (object); ++} ++ ++GQuark gdm_smartcard_error_quark (void) ++{ ++ static GQuark error_quark = 0; ++ ++ if (error_quark == 0) { ++ error_quark = g_quark_from_static_string ("gdm-smartcard-error-quark"); ++ } ++ ++ return error_quark; ++} ++ ++GdmSmartcard * ++_gdm_smartcard_new (SECMODModule *module, ++ CK_SLOT_ID slot_id, ++ int slot_series) ++{ ++ GdmSmartcard *card; ++ ++ g_return_val_if_fail (module != NULL, NULL); ++ g_return_val_if_fail (slot_id >= 1, NULL); ++ g_return_val_if_fail (slot_series > 0, NULL); ++ g_return_val_if_fail (sizeof (gulong) == sizeof (slot_id), NULL); ++ ++ card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD, ++ "module", module, ++ "slot-id", (gulong) slot_id, ++ "slot-series", slot_series, ++ NULL)); ++ return card; ++} ++ ++GdmSmartcard * ++_gdm_smartcard_new_from_name (SECMODModule *module, ++ const char *name) ++{ ++ GdmSmartcard *card; ++ ++ g_return_val_if_fail (module != NULL, NULL); ++ g_return_val_if_fail (name != NULL, NULL); ++ ++ card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD, ++ "module", module, ++ "name", name, ++ NULL)); ++ return card; ++} ++ ++void ++_gdm_smartcard_set_state (GdmSmartcard *card, ++ GdmSmartcardState state) ++{ ++ /* gdm_smartcard_fetch_certificates (card); */ ++ if (card->priv->state != state) { ++ card->priv->state = state; ++ ++ if (state == GDM_SMARTCARD_STATE_INSERTED) { ++ g_signal_emit (card, gdm_smartcard_signals[INSERTED], 0); ++ } else if (state == GDM_SMARTCARD_STATE_REMOVED) { ++ g_signal_emit (card, gdm_smartcard_signals[REMOVED], 0); ++ } else { ++ g_assert_not_reached (); ++ } ++ } ++} ++ ++/* So we could conceivably make the closure data a pointer to the card ++ * or something similiar and then emit signals when we want passwords, ++ * but it's probably easier to just get the password up front and use ++ * it. So we just take the passed in g_malloc'd (well probably, who knows) ++ * and strdup it using NSPR's memory allocation routines. ++ */ ++static char * ++gdm_smartcard_password_handler (PK11SlotInfo *slot, ++ PRBool is_retrying, ++ const char *password) ++{ ++ if (is_retrying) { ++ return NULL; ++ } ++ ++ return password != NULL? PL_strdup (password): NULL; ++} ++ ++gboolean ++gdm_smartcard_unlock (GdmSmartcard *card, ++ const char *password) ++{ ++ SECStatus status; ++ ++ PK11_SetPasswordFunc ((PK11PasswordFunc) gdm_smartcard_password_handler); ++ ++ /* we pass PR_TRUE to load certificates ++ */ ++ status = PK11_Authenticate (card->priv->slot, PR_TRUE, (gpointer) password); ++ ++ if (status != SECSuccess) { ++ g_debug ("could not unlock card - %d", status); ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static PK11SlotInfo * ++gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card, ++ const char *card_name) ++{ ++ int i; ++ ++ for (i = 0; i < card->priv->module->slotCount; i++) { ++ const char *slot_card_name; ++ ++ slot_card_name = PK11_GetTokenName (card->priv->module->slots[i]); ++ ++ if ((slot_card_name != NULL) && ++ (strcmp (slot_card_name, card_name) == 0)) { ++ return card->priv->module->slots[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static PK11SlotInfo * ++gdm_smartcard_find_slot_from_id (GdmSmartcard *card, ++ int slot_id) ++{ ++ int i; ++ ++ for (i = 0; i < card->priv->module->slotCount; i++) { ++ if (PK11_GetSlotID (card->priv->module->slots[i]) == slot_id) { ++ return card->priv->module->slots[i]; ++ } ++ } ++ ++ return NULL; ++} +diff --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 ++#include ++ ++#include ++ ++G_BEGIN_DECLS ++#define GDM_TYPE_SMARTCARD (gdm_smartcard_get_type ()) ++#define GDM_SMARTCARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD, GdmSmartcard)) ++#define GDM_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD, GdmSmartcardClass)) ++#define GDM_IS_SMARTCARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD)) ++#define GDM_IS_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD)) ++#define GDM_SMARTCARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD, GdmSmartcardClass)) ++#define GDM_SMARTCARD_ERROR (gdm_smartcard_error_quark ()) ++typedef struct _GdmSmartcardClass GdmSmartcardClass; ++typedef struct _GdmSmartcard GdmSmartcard; ++typedef struct _GdmSmartcardPrivate GdmSmartcardPrivate; ++typedef enum _GdmSmartcardError GdmSmartcardError; ++typedef enum _GdmSmartcardState GdmSmartcardState; ++ ++typedef struct _GdmSmartcardRequest GdmSmartcardRequest; ++ ++struct _GdmSmartcard { ++ GObject parent; ++ ++ /*< private > */ ++ GdmSmartcardPrivate *priv; ++}; ++ ++struct _GdmSmartcardClass { ++ GObjectClass parent_class; ++ ++ void (* inserted) (GdmSmartcard *card); ++ void (* removed) (GdmSmartcard *card); ++}; ++ ++enum _GdmSmartcardError { ++ GDM_SMARTCARD_ERROR_GENERIC = 0, ++}; ++ ++enum _GdmSmartcardState { ++ GDM_SMARTCARD_STATE_INSERTED = 0, ++ GDM_SMARTCARD_STATE_REMOVED, ++}; ++ ++GType gdm_smartcard_get_type (void) G_GNUC_CONST; ++GQuark gdm_smartcard_error_quark (void) G_GNUC_CONST; ++ ++CK_SLOT_ID gdm_smartcard_get_slot_id (GdmSmartcard *card); ++gint gdm_smartcard_get_slot_series (GdmSmartcard *card); ++GdmSmartcardState gdm_smartcard_get_state (GdmSmartcard *card); ++ ++char *gdm_smartcard_get_name (GdmSmartcard *card); ++gboolean gdm_smartcard_is_login_card (GdmSmartcard *card); ++ ++gboolean gdm_smartcard_unlock (GdmSmartcard *card, ++ const char *password); ++ ++/* don't under any circumstances call these functions */ ++#ifdef GDM_SMARTCARD_ENABLE_INTERNAL_API ++ ++GdmSmartcard *_gdm_smartcard_new (SECMODModule *module, ++ CK_SLOT_ID slot_id, ++ gint slot_series); ++GdmSmartcard *_gdm_smartcard_new_from_name (SECMODModule *module, ++ const char *name); ++ ++void _gdm_smartcard_set_state (GdmSmartcard *card, ++ GdmSmartcardState state); ++#endif ++ ++G_END_DECLS ++#endif /* GDM_SMARTCARD_H */ +diff --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/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 @@ ++ ++ ++ ++ ++ True ++ vertical ++ ++ ++ True ++ ++ ++ True ++ ++ ++ False ++ False ++ 0 ++ ++ ++ ++ ++ True ++ True ++ True ++ ++ ++ 1 ++ ++ ++ ++ ++ True ++ True ++ 0 ++ ++ ++ ++ ++ True ++ ++ ++ True ++ ++ ++ 0 ++ ++ ++ ++ ++ True ++ True ++ 1 ++ ++ ++ ++ +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 ++ * ++ */ ++ ++#include "gdm-smartcard-extension.h" ++ ++#include ++#include ++ ++GdmGreeterExtension * ++gdm_greeter_plugin_get_extension (void) ++{ ++ static GObject *extension; ++ ++ if (extension != NULL) { ++ g_object_ref (extension); ++ } else { ++ extension = g_object_new (GDM_TYPE_SMARTCARD_EXTENSION, NULL); ++ g_object_add_weak_pointer (extension, (gpointer *) &extension); ++ } ++ ++ return GDM_GREETER_EXTENSION (extension); ++} +-- +1.6.2.2 + + +From 13aad0517da16305096619edf7039666ce219b08 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 23 Feb 2009 17:57:06 -0500 +Subject: [PATCH 23/33] 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 e44122c..a7cda4e 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -2462,9 +2462,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 { +@@ -2475,6 +2472,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 7f5eb1c..7c74ecf 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 fae73be..e6c608a 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 621d1ab..f09b4e4 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.6.2.2 + + +From 2680b95658e405c6dd8bdfdabc8f65563c145848 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 24 Feb 2009 15:12:35 -0500 +Subject: [PATCH 24/33] 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 a7cda4e..3f4c031 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -1612,29 +1612,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); + +@@ -1644,16 +1664,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 +@@ -1942,7 +1957,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 b1cb281..e0737b8 100644 +--- a/gui/simple-greeter/gdm-user-chooser-widget.c ++++ b/gui/simple-greeter/gdm-user-chooser-widget.c +@@ -238,9 +238,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 7c74ecf..63f6ef3 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.6.2.2 + + +From be32bed9081cd6dfedea8fdbaa488693823edc11 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 27 Feb 2009 15:44:13 -0500 +Subject: [PATCH 25/33] 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 3f4c031..782cee8 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -803,6 +803,7 @@ reset_dialog (GdmGreeterLoginWindow *login_window) + set_message (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)); +@@ -1632,6 +1633,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)); +@@ -1665,6 +1667,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 f09b4e4..8e87db6 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.6.2.2 + + +From 3889d41f9eae0838dfc0e7e0e0bedefe3f649e74 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Mar 2009 11:10:28 -0500 +Subject: [PATCH 26/33] Only show task list if user is selected + +--- + gui/simple-greeter/gdm-greeter-login-window.c | 52 +++++++++++++++++-------- + gui/simple-greeter/gdm-task-list.c | 11 ++++- + 2 files changed, 44 insertions(+), 19 deletions(-) + +diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c +index 782cee8..78e4644 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -105,7 +105,8 @@ + + enum { + MODE_SELECTION = 0, +- MODE_AUTHENTICATION ++ MODE_SINGLE_AUTHENTICATION, ++ MODE_MULTIPLE_AUTHENTICATION, + }; + + enum { +@@ -586,7 +587,8 @@ sensitize_power_buttons_timeout (GdmGreeterLoginWindow *login_window) + sensitize_widget (login_window, "suspend-button", TRUE); + sensitize_widget (login_window, "disconnect-button", TRUE); + break; +- case MODE_AUTHENTICATION: ++ case MODE_SINGLE_AUTHENTICATION: ++ case MODE_MULTIPLE_AUTHENTICATION: + break; + default: + g_assert_not_reached (); +@@ -614,6 +616,7 @@ switch_mode (GdmGreeterLoginWindow *login_window, + GtkWidget *box; + gboolean show_restart_buttons; + gboolean show_suspend_button; ++ int number_of_tasks; + + show_restart_buttons = get_show_restart_buttons (login_window); + show_suspend_button = can_suspend (login_window); +@@ -643,6 +646,8 @@ switch_mode (GdmGreeterLoginWindow *login_window, + ! login_window->priv->display_is_local); + + show_widget (login_window, "auth-page-box", FALSE); ++ show_widget (login_window, "conversation-list", FALSE); ++ gtk_widget_set_sensitive (login_window->priv->conversation_list, TRUE); + + add_sensitize_power_buttons_timeout (login_window); + sensitize_widget (login_window, "shutdown-button", FALSE); +@@ -652,13 +657,29 @@ switch_mode (GdmGreeterLoginWindow *login_window, + + default_name = NULL; + break; +- case MODE_AUTHENTICATION: ++ case MODE_SINGLE_AUTHENTICATION: ++ case MODE_MULTIPLE_AUTHENTICATION: ++ gtk_widget_set_size_request (GTK_WIDGET (login_window), ++ GTK_WIDGET (login_window)->allocation.width, ++ -1); + show_widget (login_window, "cancel-button", TRUE); + show_widget (login_window, "shutdown-button", FALSE); + show_widget (login_window, "restart-button", FALSE); + show_widget (login_window, "suspend-button", FALSE); + show_widget (login_window, "disconnect-button", FALSE); + show_widget (login_window, "auth-page-box", TRUE); ++ ++ number_of_tasks = gdm_task_list_get_number_of_tasks (GDM_TASK_LIST (login_window->priv->conversation_list)); ++ show_widget (login_window, "conversation-list", number_of_tasks > 1); ++ ++ if (number == MODE_SINGLE_AUTHENTICATION) { ++ g_debug ("GdmGreeterLoginWindow: Single authentication, 1 task"); ++ gtk_widget_set_sensitive (login_window->priv->conversation_list, FALSE); ++ } else { ++ g_debug ("GdmGreeterLoginWindow: Multiple authentication, %d tasks", number_of_tasks); ++ gtk_widget_set_sensitive (login_window->priv->conversation_list, TRUE); ++ } ++ + default_name = "log-in-button"; + break; + default: +@@ -1626,7 +1647,7 @@ on_user_chosen (GdmGreeterLoginWindow *login_window, + begin_task_verification_for_selected_user, + login_window); + +- switch_mode (login_window, MODE_AUTHENTICATION); ++ switch_mode (login_window, MODE_MULTIPLE_AUTHENTICATION); + } + + static void +@@ -1655,6 +1676,8 @@ on_user_chooser_activated (GdmUserChooserWidget *user_chooser, + begin_task_verification, + login_window); + g_free (item_id); ++ ++ switch_mode (login_window, MODE_MULTIPLE_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, +@@ -1667,6 +1690,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_SINGLE_AUTHENTICATION); + } else { + GdmTask *task; + +@@ -1681,18 +1706,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_SINGLE_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 +@@ -2000,7 +2024,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 = glade_xml_get_widget (login_window->priv->xml, "auth-banner-label"); + /*make_label_small_italic (login_window->priv->auth_banner_label);*/ +@@ -2039,7 +2062,8 @@ gdm_greeter_login_window_key_press_event (GtkWidget *widget, + login_window = GDM_GREETER_LOGIN_WINDOW (widget); + + if (event->keyval == GDK_Escape) { +- if (login_window->priv->dialog_mode == MODE_AUTHENTICATION) { ++ if (login_window->priv->dialog_mode == MODE_SINGLE_AUTHENTICATION || ++ login_window->priv->dialog_mode == MODE_MULTIPLE_AUTHENTICATION) { + do_cancel (GDM_GREETER_LOGIN_WINDOW (widget)); + } + } +@@ -2501,12 +2525,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 25831a6..162b784 100644 +--- a/gui/simple-greeter/gdm-task-list.c ++++ b/gui/simple-greeter/gdm-task-list.c +@@ -302,17 +302,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.6.2.2 + + +From 03c44926a11cdb18f9f5492abcee8f6b11aca892 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Mar 2009 13:53:34 -0500 +Subject: [PATCH 27/33] 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 | 110 ++++++++++++++++--------- + 1 files changed, 70 insertions(+), 40 deletions(-) + +diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c +index 78e4644..ac3767a 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -1609,6 +1609,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_MULTIPLE_AUTHENTICATION); ++} ++ + static gboolean + begin_task_verification_for_selected_user (GdmTaskList *task_list, + GdmTask *task, +@@ -1634,6 +1645,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) + { +@@ -1642,15 +1662,55 @@ on_user_chosen (GdmGreeterLoginWindow *login_window, + 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); ++ begin_verification_for_selected_user (login_window); + + switch_mode (login_window, MODE_MULTIPLE_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_SINGLE_AUTHENTICATION); ++} ++ ++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 auth conversation for item %s", 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_SINGLE_AUTHENTICATION); ++ gdm_task_list_set_active_task (GDM_TASK_LIST (login_window->priv->conversation_list), task); ++ ++ g_object_unref (task); ++} ++ ++static void + on_user_chooser_activated (GdmUserChooserWidget *user_chooser, + GdmGreeterLoginWindow *login_window) + { +@@ -1669,53 +1729,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"); +- 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_MULTIPLE_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_SINGLE_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); ++ begin_single_service_verification (login_window, item_id); + g_free (item_id); +- +- switch_mode (login_window, MODE_SINGLE_AUTHENTICATION); +- gdm_task_list_set_active_task (GDM_TASK_LIST (login_window->priv->conversation_list), task); +- +- g_object_unref (task); + } + } + +-- +1.6.2.2 + + +From f06b523bd8e19902ce38affcdffcbabc0dd4d7b0 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Mar 2009 13:57:34 -0500 +Subject: [PATCH 28/33] 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 ac3767a..fa93108 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -775,6 +775,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, +@@ -876,10 +890,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))) { +@@ -908,10 +919,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)); +@@ -979,10 +987,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), +@@ -1003,10 +1008,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), +@@ -1108,10 +1110,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), +@@ -1139,10 +1139,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), +@@ -1689,10 +1686,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.6.2.2 + + +From 32b7ce16fbb4a46369932e4b4d648135b87636b1 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Mar 2009 15:23:51 -0500 +Subject: [PATCH 29/33] Drop the different auth modes in favor of calling reset manually + +--- + gui/simple-greeter/gdm-greeter-login-window.c | 57 +++++++++++++++---------- + 1 files changed, 35 insertions(+), 22 deletions(-) + +diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c +index fa93108..ed009a4 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -105,8 +105,7 @@ + + enum { + MODE_SELECTION = 0, +- MODE_SINGLE_AUTHENTICATION, +- MODE_MULTIPLE_AUTHENTICATION, ++ MODE_AUTHENTICATION + }; + + enum { +@@ -587,8 +586,7 @@ sensitize_power_buttons_timeout (GdmGreeterLoginWindow *login_window) + sensitize_widget (login_window, "suspend-button", TRUE); + sensitize_widget (login_window, "disconnect-button", TRUE); + break; +- case MODE_SINGLE_AUTHENTICATION: +- case MODE_MULTIPLE_AUTHENTICATION: ++ case MODE_AUTHENTICATION: + break; + default: + g_assert_not_reached (); +@@ -647,7 +645,6 @@ switch_mode (GdmGreeterLoginWindow *login_window, + + show_widget (login_window, "auth-page-box", FALSE); + show_widget (login_window, "conversation-list", FALSE); +- gtk_widget_set_sensitive (login_window->priv->conversation_list, TRUE); + + add_sensitize_power_buttons_timeout (login_window); + sensitize_widget (login_window, "shutdown-button", FALSE); +@@ -657,8 +654,7 @@ switch_mode (GdmGreeterLoginWindow *login_window, + + default_name = NULL; + break; +- case MODE_SINGLE_AUTHENTICATION: +- case MODE_MULTIPLE_AUTHENTICATION: ++ case MODE_AUTHENTICATION: + gtk_widget_set_size_request (GTK_WIDGET (login_window), + GTK_WIDGET (login_window)->allocation.width, + -1); +@@ -672,14 +668,6 @@ switch_mode (GdmGreeterLoginWindow *login_window, + 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); + +- if (number == MODE_SINGLE_AUTHENTICATION) { +- g_debug ("GdmGreeterLoginWindow: Single authentication, 1 task"); +- gtk_widget_set_sensitive (login_window->priv->conversation_list, FALSE); +- } else { +- g_debug ("GdmGreeterLoginWindow: Multiple authentication, %d tasks", number_of_tasks); +- gtk_widget_set_sensitive (login_window->priv->conversation_list, TRUE); +- } +- + default_name = "log-in-button"; + break; + default: +@@ -1614,7 +1602,7 @@ begin_verification (GdmGreeterLoginWindow *login_window) + begin_task_verification, + login_window); + +- switch_mode (login_window, MODE_MULTIPLE_AUTHENTICATION); ++ switch_mode (login_window, MODE_AUTHENTICATION); + } + + static gboolean +@@ -1661,7 +1649,7 @@ on_user_chosen (GdmGreeterLoginWindow *login_window, + + begin_verification_for_selected_user (login_window); + +- switch_mode (login_window, MODE_MULTIPLE_AUTHENTICATION); ++ switch_mode (login_window, MODE_AUTHENTICATION); + } + + static void +@@ -1677,7 +1665,30 @@ begin_auto_login (GdmGreeterLoginWindow *login_window) + 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_SINGLE_AUTHENTICATION); ++ switch_mode (login_window, MODE_AUTHENTICATION); ++} ++ ++static gboolean ++reset_task_if_not_given (GdmTaskList *task_list, ++ GdmTask *task, ++ GdmTask *given_task) ++{ ++ if (task == given_task) { ++ return FALSE; ++ } ++ ++ gdm_conversation_reset (GDM_CONVERSATION (task)); ++ return FALSE; ++} ++ ++static void ++reset_every_task_but_given_task (GdmGreeterLoginWindow *login_window, ++ GdmTask *task) ++{ ++ gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list), ++ (GdmTaskListForeachFunc) ++ reset_task_if_not_given, ++ task); + } + + static void +@@ -1692,15 +1703,18 @@ begin_single_service_verification (GdmGreeterLoginWindow *login_window, + g_debug ("GdmGreeterLoginWindow: %s has no task associated with it", service_name); + return; + } +- g_debug ("GdmGreeterLoginWindow: Beginning auth conversation for item %s", service_name); ++ 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_SINGLE_AUTHENTICATION); ++ 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); + } + +@@ -2086,8 +2100,7 @@ gdm_greeter_login_window_key_press_event (GtkWidget *widget, + login_window = GDM_GREETER_LOGIN_WINDOW (widget); + + if (event->keyval == GDK_Escape) { +- if (login_window->priv->dialog_mode == MODE_SINGLE_AUTHENTICATION || +- login_window->priv->dialog_mode == MODE_MULTIPLE_AUTHENTICATION) { ++ if (login_window->priv->dialog_mode == MODE_AUTHENTICATION) { + do_cancel (GDM_GREETER_LOGIN_WINDOW (widget)); + } + } +-- +1.6.2.2 + + +From c3c9927ace5ea32c918277ec70a379ef61d421dc Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 2 Mar 2009 17:09:16 -0500 +Subject: [PATCH 30/33] 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 ed009a4..a16948f 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -2389,7 +2389,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) +@@ -2400,7 +2400,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), +@@ -2408,6 +2414,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 + + #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 8e87db6..5a2c380 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.6.2.2 + + +From 8697958c238a200a33d7954d82965d11ef36419b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 13 Apr 2009 14:19:50 -0400 +Subject: [PATCH 31/33] 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 a16948f..3daf89d 100644 +--- a/gui/simple-greeter/gdm-greeter-login-window.c ++++ b/gui/simple-greeter/gdm-greeter-login-window.c +@@ -907,6 +907,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.6.2.2 + + +From 7b65eadcc7ab206c0ac8167cc22a30cfaaf07f5a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 13 Apr 2009 16:49:12 -0400 +Subject: [PATCH 32/33] Add icon for smartcard plugin + +We were just using the green apple before. +--- + configure.ac | 3 +++ + gui/simple-greeter/plugins/smartcard/Makefile.am | 2 ++ + .../plugins/smartcard/gdm-smartcard-extension.c | 2 +- + .../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 + + 8 files changed, 17 insertions(+), 1 deletions(-) + 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 + +diff --git a/configure.ac b/configure.ac +index af5d316..aa54a9b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1411,6 +1411,9 @@ gui/simple-greeter/plugins/Makefile + gui/simple-greeter/plugins/password/Makefile + gui/simple-greeter/plugins/fingerprint/Makefile + gui/simple-greeter/plugins/smartcard/Makefile ++gui/simple-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/smartcard/Makefile.am b/gui/simple-greeter/plugins/smartcard/Makefile.am +index 15d0174..1ccebda 100644 +--- a/gui/simple-greeter/plugins/smartcard/Makefile.am ++++ b/gui/simple-greeter/plugins/smartcard/Makefile.am +@@ -1,3 +1,5 @@ ++SUBDIRS = icons ++ + NULL = + PAM_SERVICE_NAME = gdm-smartcard + +diff --git a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c +index 5a2c380..73e0f84 100644 +--- a/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c ++++ b/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c +@@ -433,7 +433,7 @@ gdm_smartcard_extension_init (GdmSmartcardExtension *extension) + GDM_TYPE_SMARTCARD_EXTENSION, + GdmSmartcardExtensionPrivate); + +- extension->priv->icon = g_themed_icon_new ("apple-green"); ++ 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/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)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXJ- +z6cQQvze?o*000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0008QNkl25h)mRS()F5IML|LeofZ5$Phq#SvEXn3)x9QHz&V1jD*OHUa=lnh|yoWc& +zo1+2m0}628RN4)kP9zfJhGEoz$TZEX{|)Htgf`%?ZQIABl!txa-_X&~;rI9Vo88^r +z1;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(}(unH3ls_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%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)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXJ- +z6calzC#000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000lgNkl2aFf(X>ozT5 +zC$5aZk)aA|*R=Bt;5o9kwWGLxmKt<-N{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#^8Cli339gNwym|C9-PB*HI@kv?`fqW>?@BILKy#D +z4S0a;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#Ugb-f=#OBSLRV)@WcJJQJ&Ye3&I2?Xcz^>Gsxim*Q?-KBt3|v`2Duq&D7y_j< +z0x%8GT2LLZXlt%!eN&J%4OM6j-+g(6iP&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{Erluyw#>N;L8e(*Gv`DLkM=6Eh@8_O-?!oKzic_afJqug{j#UWY +zI1WMx{9Y4}DUeEFnSyt9HPNv?jFgh;>FJ6cc3rnJpQx!J5{ZgwtW?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}>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@!QJRH14t>!X0sePaDb|+Dtta4 +zufP5}>(;HKy}g}yJWeK)K}yM*HET#D5_gq-z%nx;u(VMPRx)Uz}P+MC|E|(*h%P~DYjo0fXolcX@W|^3n;K?VS +z=PjT#i5aQW?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$q8NID7Ow6H`@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?~CGDRgIki0dHM>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@$4sA?5`wY$vF6CHez{E%@3`>BkDOHUut+h_2QsozaHc|NQ$fdzg?~8Hx=udg_ +z(OcQlR#*Q2Q;*%gl=Cmz42>tbG(5-E@f1>O6q; +zX-_O5v;07EtbcS_^-VG|xmachDJ7OAFfh5jGlJ`CydHyHoz3{XW +z{(dz$IAT2ho_d-#6|#4&hB1V1g$WsgB6x$QI*YsCwN&|#S%sTbNE@USNTtgHIC@#} +zG8FJ)ngXp0H6j3yX`s=3WJoXpKFL!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-zHPRK--Qpu^f!9}X+|e<~ +z6MvoI@K5qN;7er_x_I#-M~@z*udk0IM~=vunHdwn +Date: Mon, 13 Apr 2009 16:50:11 -0400 +Subject: [PATCH 33/33] Add icon for fingerprint plugin + +We were just using some random one before +--- + configure.ac | 3 +++ + gui/simple-greeter/plugins/fingerprint/Makefile.am | 2 ++ + .../fingerprint/gdm-fingerprint-extension.c | 2 +- + .../plugins/fingerprint/icons/16x16/Makefile.am | 5 +++++ + .../plugins/fingerprint/icons/48x48/Makefile.am | 5 +++++ + .../plugins/fingerprint/icons/Makefile.am | 1 + + 6 files changed, 17 insertions(+), 1 deletions(-) + create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am + create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am + create mode 100644 gui/simple-greeter/plugins/fingerprint/icons/Makefile.am + +diff --git a/configure.ac b/configure.ac +index aa54a9b..287dfa5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1410,6 +1410,9 @@ gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc + gui/simple-greeter/plugins/Makefile + gui/simple-greeter/plugins/password/Makefile + gui/simple-greeter/plugins/fingerprint/Makefile ++gui/simple-greeter/plugins/fingerprint/icons/Makefile ++gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile ++gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile + gui/simple-greeter/plugins/smartcard/Makefile + gui/simple-greeter/plugins/smartcard/icons/Makefile + gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile +diff --git a/gui/simple-greeter/plugins/fingerprint/Makefile.am b/gui/simple-greeter/plugins/fingerprint/Makefile.am +index d5f7e28..25fb6e8 100644 +--- a/gui/simple-greeter/plugins/fingerprint/Makefile.am ++++ b/gui/simple-greeter/plugins/fingerprint/Makefile.am +@@ -1,3 +1,5 @@ ++SUBDIRS = icons ++ + NULL = + PAM_SERVICE_NAME = gdm-fingerprint + +diff --git a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c +index 63f6ef3..316ef46 100644 +--- a/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c ++++ b/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c +@@ -297,7 +297,7 @@ gdm_fingerprint_extension_init (GdmFingerprintExtension *extension) + GDM_TYPE_FINGERPRINT_EXTENSION, + GdmFingerprintExtensionPrivate); + +- extension->priv->icon = g_themed_icon_new ("stock_allow-effects"); ++ 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/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/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/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 +-- +1.6.2.2 + diff --git a/gdm-fingerprint-16.png b/gdm-fingerprint-16.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`n0t7U_A+O~xmQ|0`3{N*Pgg&ebxsLQ0N@R| ASO5S3 literal 0 HcmV?d00001 diff --git a/gdm-fingerprint-48.png b/gdm-fingerprint-48.png new file mode 100644 index 0000000000000000000000000000000000000000..fd6f546c387db5dbce1a88cb4bb46c3bdaa7f4a5 GIT binary patch literal 1638 zcmV-s2ATPZP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m_e01m_fl`9S#00007bV*G`2iXJ- z6EzRg;agMy00rhrL_t(&-sPBGY!p=#$N%TDp+=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<(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?@ot!$1YP1o zLqq56%D{k$dW{}z&WuMAA|pUwYiny#pOoE(wl(eT?Fm}|h{$a7DZQOfd3kvWfF%ZX z8vs;QRXLg2c&MwfbR#n?ShMZFK$4^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(QvPD(ZGqNn_z$Otw zyrw8hH2{yHL_eL=VJ;etUIuX31WZ;GMYu&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/gdm-fingerprint.pam b/gdm-fingerprint.pam new file mode 100644 index 0000000..f0d844c --- /dev/null +++ b/gdm-fingerprint.pam @@ -0,0 +1,15 @@ +auth substack fingerprint-auth +auth required pam_succeed_if.so user != root quiet + +account required pam_nologin.so +account include fingerprint-auth + +password include fingerprint-auth + +session required pam_selinux.so close +session required pam_loginuid.so +session optional pam_console.so +session required pam_selinux.so open +session optional pam_keyinit.so force revoke +session required pam_namespace.so +session include fingerprint-auth diff --git a/gdm-password.pam b/gdm-password.pam new file mode 100644 index 0000000..4eacbe9 --- /dev/null +++ b/gdm-password.pam @@ -0,0 +1,17 @@ +auth substack password-auth +auth required pam_succeed_if.so user != root quiet +auth optional pam_gnome_keyring.so + +account required pam_nologin.so +account include password-auth + +password include password-auth + +session required pam_selinux.so close +session required pam_loginuid.so +session optional pam_console.so +session required pam_selinux.so open +session optional pam_keyinit.so force revoke +session required pam_namespace.so +session optional pam_gnome_keyring.so auto_start +session include password-auth diff --git a/gdm-smartcard-16.png b/gdm-smartcard-16.png new file mode 100644 index 0000000000000000000000000000000000000000..0112af1b8d891ad312f9fcc3bf6074df3e003359 GIT binary patch literal 871 zcmV-t1DO1YP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXJ- z6cQQvze?o*000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0008QNkl25h)mRS()F5IML|LeofZ5$Phq#SvEXn3)x9QHz&V1jD*OHUa=lnh|yoWc& zo1+2m0}628RN4)kP9zfJhGEoz$TZEX{|)Htgf`%?ZQIABl!txa-_X&~;rI9Vo88^r z1;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(}(unH3ls_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%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/gdm-smartcard-48.png b/gdm-smartcard-48.png new file mode 100644 index 0000000000000000000000000000000000000000..35d5578d9ec83ae6b17aa7230c1b4df34e13e2cc GIT binary patch literal 4202 zcmV-w5S8zVP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXJ- z6calzC#000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000lgNkl2aFf(X>ozT5 zC$5aZk)aA|*R=Bt;5o9kwWGLxmKt<-N{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#^8Cli339gNwym|C9-PB*HI@kv?`fqW>?@BILKy#D z4S0a;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#Ugb-f=#OBSLRV)@WcJJQJ&Ye3&I2?Xcz^>Gsxim*Q?-KBt3|v`2Duq&D7y_j< z0x%8GT2LLZXlt%!eN&J%4OM6j-+g(6iP&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{Erluyw#>N;L8e(*Gv`DLkM=6Eh@8_O-?!oKzic_afJqug{j#UWY zI1WMx{9Y4}DUeEFnSyt9HPNv?jFgh;>FJ6cc3rnJpQx!J5{ZgwtW?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}>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@!QJRH14t>!X0sePaDb|+Dtta4 zufP5}>(;HKy}g}yJWeK)K}yM*HET#D5_gq-z%nx;u(VMPRx)Uz}P+MC|E|(*h%P~DYjo0fXolcX@W|^3n;K?VS z=PjT#i5aQW?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$q8NID7Ow6H`@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?~CGDRgIki0dHM>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@$4sA?5`wY$vF6CHez{E%@3`>BkDOHUut+h_2QsozaHc|NQ$fdzg?~8Hx=udg_ z(OcQlR#*Q2Q;*%gl=Cmz42>tbG(5-E@f1>O6q; zX-_O5v;07EtbcS_^-VG|xmachDJ7OAFfh5jGlJ`CydHyHoz3{XW z{(dz$IAT2ho_d-#6|#4&hB1V1g$WsgB6x$QI*YsCwN&|#S%sTbNE@USNTtgHIC@#} zG8FJ)ngXp0H6j3yX`s=3WJoXpKFL!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-zHPRK--Qpu^f!9}X+|e<~ z6MvoI@K5qN;7er_x_I#-M~@z*udk0IM~=vunHdwn= 0:2.21.9-0 Obsoletes: fast-user-switch-applet Provides: fast-user-switch-applet = %{epoch}:%{version}-%{release} +%package plugin-smartcard +Summary: GDM smartcard plugin +Group: User Interface/Desktops +Requires: gdm = %{epoch}:%{version}-%{release} + +%package plugin-fingerprint +Summary: GDM fingerprint plugin +Group: User Interface/Desktops +Requires: gdm = %{epoch}:%{version}-%{release} + %description GDM provides the graphical login screen, shown shortly after boot up, log out, and when user switching. @@ -118,6 +134,12 @@ log out, and when user switching. The GDM user switcher applet provides a mechanism for changing among multiple simulanteous logged in users. +%description plugin-smartcard +The GDM smartcard plugin provides functionality necessary to use a smart card with GDM. + +%description plugin-fingerprint +The GDM fingerprint plugin provides functionality necessary to use a fingerprint reader with GDM. + %prep %setup -q %patch2 -p1 -b .force-active-vt @@ -125,11 +147,11 @@ multiple simulanteous logged in users. %patch4 -p1 -b .append-logs %patch13 -p1 -b .system-keyboard -%patch14 -p1 -b .multistack-but-boring %patch15 -p1 -b .start-faster %patch16 -p1 -b .dont-depend-on-hostname %patch17 -p1 -b .clean-up-auth-entries %patch18 -p1 -b .load-settings-for-other-user +%patch19 -p1 -b .multistack %patch99 -p1 -b .fedora-logo @@ -138,7 +160,13 @@ autoreconf -i -f %build cp -f %{SOURCE1} data/gdm cp -f %{SOURCE2} data/gdm-autologin -cp -f %{SOURCE3} utils/gdmsetup-pam +cp -f %{SOURCE3} gui/simple-greeter/plugins/password/gdm-password.pam +cp -f %{SOURCE4} gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam +cp -f %{SOURCE5} gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam +cp -f %{SOURCE6} gui/simple-greeter/plugins/smartcard/icons/16x16/gdm-smartcard.png +cp -f %{SOURCE7} gui/simple-greeter/plugins/smartcard/icons/48x48/gdm-smartcard.png +cp -f %{SOURCE8} gui/simple-greeter/plugins/fingerprint/icons/16x16/gdm-fingerprint.png +cp -f %{SOURCE9} gui/simple-greeter/plugins/fingerprint/icons/48x48/gdm-fingerprint.png %configure --with-pam-prefix=%{_sysconfdir} \ --enable-profiling \ @@ -192,7 +220,6 @@ find $RPM_BUILD_ROOT -name '*.la' -delete rm -f $RPM_BUILD_ROOT%{_includedir}/gdm/simple-greeter/gdm-greeter-extension.h rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/gdmsimplegreeter.pc -rm -f $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/gdm-password %find_lang gdm --with-gnome @@ -297,6 +324,7 @@ fi %config %{_sysconfdir}/gdm/PostSession/* %config %{_sysconfdir}/pam.d/gdm %config %{_sysconfdir}/pam.d/gdm-autologin +%config %{_sysconfdir}/pam.d/gdm-password # not config files %{_sysconfdir}/gdm/Xsession %{_sysconfdir}/gdm/gdm.schemas @@ -345,14 +373,29 @@ fi %attr(1770, root, gdm) %dir %{_localstatedir}/gdm %attr(1777, root, gdm) %dir %{_localstatedir}/run/gdm - %files user-switch-applet %defattr(-, root, root) %{_libexecdir}/gdm-user-switch-applet %{_libdir}/bonobo/servers/GNOME_FastUserSwitchApplet.server %{_datadir}/gnome-2.0/ui/GNOME_FastUserSwitchApplet.xml +%files plugin-smartcard +%defattr(-, root, root) +%config %{_sysconfdir}/pam.d/gdm-smartcard +%{_datadir}/gdm/simple-greeter/extensions/smartcard/page.ui +%{_libdir}/gdm/simple-greeter/plugins/smartcard.so +%{_libexecdir}/gdm-smartcard-worker + +%files plugin-fingerprint +%defattr(-, root, root) +%config %{_sysconfdir}/pam.d/gdm-fingerprint +%{_datadir}/gdm/simple-greeter/extensions/fingerprint/page.ui +%{_libdir}/gdm/simple-greeter/plugins/fingerprint.so + %changelog +* Mon Apr 13 2009 Ray Strode - 1:2.26.0-8 +- Add less boring multistack patch for testing + * Mon Mar 23 2009 Ray Strode - 1:2.26.0-7 - Load session and language settings when username is read on Other user