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