summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/gdm-marshal.list1
-rw-r--r--configure.ac36
-rw-r--r--gui/simple-greeter/Makefile.am15
-rw-r--r--gui/simple-greeter/extensions/Makefile.am5
-rw-r--r--gui/simple-greeter/extensions/password/Makefile.am51
-rw-r--r--gui/simple-greeter/extensions/password/gdm-password19
-rw-r--r--gui/simple-greeter/extensions/password/gdm-password-extension.c446
-rw-r--r--gui/simple-greeter/extensions/password/gdm-password-extension.h56
-rw-r--r--gui/simple-greeter/extensions/password/gdm-password.pam19
-rw-r--r--gui/simple-greeter/extensions/password/page.ui57
-rw-r--r--gui/simple-greeter/extensions/unified/Makefile.am42
-rw-r--r--gui/simple-greeter/extensions/unified/gdm-unified-extension.c441
-rw-r--r--gui/simple-greeter/extensions/unified/gdm-unified-extension.h57
-rw-r--r--gui/simple-greeter/extensions/unified/gdm.pam12
-rw-r--r--gui/simple-greeter/extensions/unified/page.ui57
-rw-r--r--gui/simple-greeter/gdm-extension-list.c388
-rw-r--r--gui/simple-greeter/gdm-extension-list.h70
-rw-r--r--gui/simple-greeter/gdm-greeter-client.c21
-rw-r--r--gui/simple-greeter/gdm-greeter-client.h2
-rw-r--r--gui/simple-greeter/gdm-greeter-login-window.c1342
-rw-r--r--gui/simple-greeter/gdm-greeter-login-window.h24
-rw-r--r--gui/simple-greeter/gdm-greeter-login-window.ui67
-rw-r--r--gui/simple-greeter/gdm-greeter-session.c97
-rw-r--r--gui/simple-greeter/gdm-user-chooser-widget.c23
-rw-r--r--gui/simple-greeter/libgdmsimplegreeter/Makefile.am43
-rw-r--r--gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.c278
-rw-r--r--gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.h128
-rw-r--r--gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in11
-rw-r--r--po/POTFILES.in1
29 files changed, 3431 insertions, 378 deletions
diff --git a/common/gdm-marshal.list b/common/gdm-marshal.list
index d5455e1d..d8a9e728 100644
--- a/common/gdm-marshal.list
+++ b/common/gdm-marshal.list
@@ -5,3 +5,4 @@ VOID:STRING,STRING
VOID:UINT,UINT
VOID:STRING,INT
VOID:DOUBLE
+BOOLEAN:STRING
diff --git a/configure.ac b/configure.ac
index df47521e..475a4761 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,22 @@ AC_PROG_CXX
AM_PROG_CC_C_O
AC_PROG_LIBTOOL()
+## increment if the plugin interface has additions, changes, removals.
+LT_CURRENT=1
+
+## increment any time the source changes; set to
+## 0 if you increment CURRENT
+LT_REVISION=0
+
+## increment if any interfaces have been added; set to 0
+## if any interfaces have been changed or removed. removal has
+## precedence over adding, so set to 0 if both happened.
+LT_AGE=0
+
+AC_SUBST(LT_CURRENT)
+AC_SUBST(LT_REVISION)
+AC_SUBST(LT_AGE)
+
AC_HEADER_STDC
AC_SUBST(VERSION)
@@ -76,6 +92,8 @@ PKG_CHECK_MODULES(DAEMON,
AC_SUBST(DAEMON_CFLAGS)
AC_SUBST(DAEMON_LIBS)
+GLIB_GSETTINGS
+
PKG_CHECK_MODULES(XLIB, x11 xau xrandr, ,
[AC_PATH_XTRA
if test "x$no_x" = xyes; then
@@ -195,6 +213,11 @@ AC_SUBST(dmconfdir)
dnl ---------------------------------------------------------------------------
dnl - Configure arguments
dnl ---------------------------------------------------------------------------
+AC_ARG_ENABLE(split-authentication,
+ AS_HELP_STRING([--enable-split-authentication],
+ [Enable multiple simultaneous PAM conversations during login @<:@default=yes@:>@]),,
+ enable_split_authentication=no)
+AM_CONDITIONAL(ENABLE_SPLIT_AUTHENTICATION, test x$enable_split_authentication = xyes)
AC_ARG_ENABLE(console-helper,
AS_HELP_STRING([--enable-console-helper],
@@ -1259,6 +1282,14 @@ fi
AC_SUBST(GDM_SCREENSHOT_DIR)
+dnl ---------------------------------------------------------------------------
+dnl - Directory for simple greeter extensions
+dnl ---------------------------------------------------------------------------
+GDM_SIMPLE_GREETER_PLUGINS_DIR=${libdir}/gdm/simple-greeter/extensions
+AC_SUBST(GDM_SIMPLE_GREETER_PLUGINS_DIR)
+
+GDM_SIMPLE_GREETER_EXTENSIONS_DATA_DIR=${datadir}/gdm/simple-greeter/extensions
+AC_SUBST(GDM_SIMPLE_GREETER_EXTENSIONS_DATA_DIR)
dnl ---------------------------------------------------------------------------
dnl - Finish
@@ -1384,6 +1415,11 @@ daemon/Makefile
docs/Makefile
gui/Makefile
gui/simple-greeter/Makefile
+gui/simple-greeter/libgdmsimplegreeter/Makefile
+gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc
+gui/simple-greeter/extensions/Makefile
+gui/simple-greeter/extensions/unified/Makefile
+gui/simple-greeter/extensions/password/Makefile
gui/simple-chooser/Makefile
utils/Makefile
data/gdm.conf
diff --git a/gui/simple-greeter/Makefile.am b/gui/simple-greeter/Makefile.am
index a842c7bd..8ee0cee4 100644
--- a/gui/simple-greeter/Makefile.am
+++ b/gui/simple-greeter/Makefile.am
@@ -1,8 +1,13 @@
NULL =
+SUBDIRS = \
+ libgdmsimplegreeter \
+ extensions \
+ $(NULL)
AM_CPPFLAGS = \
-I$(top_srcdir)/common \
-I$(top_builddir)/common \
+ -I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter \
-DDMCONFDIR=\""$(dmconfdir)"\" \
-DGDMCONFDIR=\"$(gdmconfdir)\" \
-DDATADIR=\""$(datadir)"\" \
@@ -15,6 +20,7 @@ AM_CPPFLAGS = \
-DGDM_CACHE_DIR=\""$(localstatedir)/cache/gdm"\" \
-DAT_SPI_REGISTRYD_DIR="\"$(AT_SPI_REGISTRYD_DIR)\"" \
$(UPOWER_CFLAGS) \
+ -DGDM_SIMPLE_GREETER_PLUGINS_DIR="\"$(GDM_SIMPLE_GREETER_PLUGINS_DIR)\""\
$(DISABLE_DEPRECATED_CFLAGS) \
$(GTK_CFLAGS) \
$(SIMPLE_GREETER_CFLAGS) \
@@ -60,10 +66,14 @@ test_greeter_login_window_SOURCES = \
gdm-user-chooser-widget.c \
gdm-user-chooser-dialog.h \
gdm-user-chooser-dialog.c \
+ gdm-extension-list.h \
+ gdm-extension-list.c \
$(NULL)
test_greeter_login_window_LDADD = \
$(top_builddir)/common/libgdmcommon.la \
+ $(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la \
+ $(top_builddir)/gui/simple-greeter/extensions/unified/libunified.la \
$(COMMON_LIBS) \
$(SIMPLE_GREETER_LIBS) \
$(RBAC_LIBS) \
@@ -104,6 +114,7 @@ test_greeter_panel_SOURCES = \
test_greeter_panel_LDADD = \
$(top_builddir)/common/libgdmcommon.la \
+ $(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la \
$(SIMPLE_GREETER_LIBS) \
$(GTK_LIBS) \
$(GCONF_LIBS) \
@@ -251,10 +262,14 @@ gdm_simple_greeter_SOURCES = \
gdm-session-option-widget.c \
gdm-user-chooser-widget.h \
gdm-user-chooser-widget.c \
+ gdm-extension-list.h \
+ gdm-extension-list.c \
$(NULL)
gdm_simple_greeter_LDADD = \
$(top_builddir)/common/libgdmcommon.la \
+ $(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la \
+ $(top_builddir)/gui/simple-greeter/extensions/unified/libunified.la \
$(COMMON_LIBS) \
$(EXTRA_GREETER_LIBS) \
$(SIMPLE_GREETER_LIBS) \
diff --git a/gui/simple-greeter/extensions/Makefile.am b/gui/simple-greeter/extensions/Makefile.am
new file mode 100644
index 00000000..d636be33
--- /dev/null
+++ b/gui/simple-greeter/extensions/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = unified
+
+if ENABLE_SPLIT_AUTHENTICATION
+SUBDIRS += password
+endif
diff --git a/gui/simple-greeter/extensions/password/Makefile.am b/gui/simple-greeter/extensions/password/Makefile.am
new file mode 100644
index 00000000..13898de6
--- /dev/null
+++ b/gui/simple-greeter/extensions/password/Makefile.am
@@ -0,0 +1,51 @@
+NULL =
+PAM_SERVICE_NAME = gdm-password
+
+extensiondir = $(GDM_SIMPLE_GREETER_EXTENSIONS_DATA_DIR)/password
+extension_DATA = page.ui
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter \
+ -DDMCONFDIR=\""$(dmconfdir)"\" \
+ -DGDMCONFDIR=\"$(gdmconfdir)\" \
+ -DPLUGINDATADIR=\""$(extensiondir)"\" \
+ -DGDM_PASSWORD_EXTENSION_SERVICE_NAME=\""$(PAM_SERVICE_NAME)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DSBINDIR=\""$(sbindir)"\" \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(SIMPLE_GREETER_CFLAGS) \
+ $(POLKIT_GNOME_CFLAGS) \
+ $(NULL)
+
+plugindir = $(GDM_SIMPLE_GREETER_PLUGINS_DIR)
+plugin_LTLIBRARIES = libpassword.la
+
+libpassword_la_CFLAGS = \
+ $(SIMPLE_GREETER_CFLAGS) \
+ $(NULL)
+
+libpassword_la_LDFLAGS = -module -avoid-version -export-dynamic
+libpassword_la_LIBADD = ../../../../common/libgdmcommon.la \
+ ../../libgdmsimplegreeter/libgdmsimplegreeter.la
+libpassword_la_SOURCES = \
+ gdm-password-extension.h \
+ gdm-password-extension.c
+
+$(PAM_SERVICE_NAME): $(PAM_SERVICE_NAME).pam
+ cp $(PAM_SERVICE_NAME).pam $(PAM_SERVICE_NAME)
+
+pamdir = $(PAM_PREFIX)/pam.d
+pam_DATA = $(PAM_SERVICE_NAME)
+
+EXTRA_DIST = $(extension_DATA) $(PAM_SERVICE_NAME).pam
+CLEANFILES = $(PAM_SERVICE_NAME)
+
+MAINTAINERCLEANFILES = \
+ *~ \
+ $(PAM_SERVICE_NAME) \
+ Makefile.in
diff --git a/gui/simple-greeter/extensions/password/gdm-password b/gui/simple-greeter/extensions/password/gdm-password
new file mode 100644
index 00000000..bac431d3
--- /dev/null
+++ b/gui/simple-greeter/extensions/password/gdm-password
@@ -0,0 +1,19 @@
+# Sample PAM file for doing password authentication.
+# Distros should replace this with what makes sense for them.
+auth required pam_env.so
+auth sufficient pam_unix.so nullok try_first_pass
+auth requisite pam_succeed_if.so uid >= 500 quiet
+auth required pam_deny.so
+
+account required pam_unix.so
+account sufficient pam_localuser.so
+account sufficient pam_succeed_if.so uid < 500 quiet
+account required pam_permit.so
+
+password requisite pam_cracklib.so try_first_pass retry=3 type=
+password sufficient pam_unix.so nullok try_first_pass use_authtok
+password required pam_deny.so
+
+session optional pam_keyinit.so revoke
+session required pam_limits.so
+session required pam_unix.so
diff --git a/gui/simple-greeter/extensions/password/gdm-password-extension.c b/gui/simple-greeter/extensions/password/gdm-password-extension.c
new file mode 100644
index 00000000..6391db16
--- /dev/null
+++ b/gui/simple-greeter/extensions/password/gdm-password-extension.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-password-extension.h"
+#include "gdm-login-extension.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+struct _GdmPasswordExtensionPrivate
+{
+ GIcon *icon;
+ GtkWidget *page;
+ GtkActionGroup *actions;
+ GtkAction *login_action;
+
+ GtkWidget *message_label;
+ GtkWidget *prompt_label;
+ GtkWidget *prompt_entry;
+
+ GQueue *message_queue;
+ guint message_timeout_id;
+
+ guint answer_pending : 1;
+};
+
+typedef struct {
+ char *text;
+ GdmServiceMessageType type;
+} QueuedMessage;
+
+static void gdm_password_extension_finalize (GObject *object);
+
+static void gdm_login_extension_iface_init (GdmLoginExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmPasswordExtension,
+ gdm_password_extension,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GDM_TYPE_LOGIN_EXTENSION,
+ gdm_login_extension_iface_init));
+
+static void
+set_message (GdmPasswordExtension *extension,
+ const char *message)
+{
+ gtk_widget_show (extension->priv->message_label);
+ gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+free_queued_message (QueuedMessage *message)
+{
+ g_free (message->text);
+ g_slice_free (QueuedMessage, message);
+}
+
+static void
+purge_message_queue (GdmPasswordExtension *extension)
+{
+ if (extension->priv->message_timeout_id) {
+ g_source_remove (extension->priv->message_timeout_id);
+ extension->priv->message_timeout_id = 0;
+ }
+ g_queue_foreach (extension->priv->message_queue,
+ (GFunc) free_queued_message,
+ NULL);
+ g_queue_clear (extension->priv->message_queue);
+}
+
+static gboolean
+dequeue_message (GdmPasswordExtension *extension)
+{
+ if (!g_queue_is_empty (extension->priv->message_queue)) {
+ int duration;
+ gboolean needs_beep;
+
+ QueuedMessage *message;
+ message = (QueuedMessage *) g_queue_pop_head (extension->priv->message_queue);
+
+ switch (message->type) {
+ case GDM_SERVICE_MESSAGE_TYPE_INFO:
+ needs_beep = FALSE;
+ break;
+ case GDM_SERVICE_MESSAGE_TYPE_PROBLEM:
+ needs_beep = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ set_message (extension, message->text);
+
+ duration = (int) (g_utf8_strlen (message->text, -1) / 66.0) * 1000;
+ duration = CLAMP (duration, 400, 3000);
+
+ extension->priv->message_timeout_id = g_timeout_add (duration,
+ (GSourceFunc) dequeue_message,
+ extension);
+ if (needs_beep) {
+ gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (extension->priv->page)));
+ }
+
+ free_queued_message (message);
+ } else {
+ extension->priv->message_timeout_id = 0;
+
+ _gdm_login_extension_emit_message_queue_empty (GDM_LOGIN_EXTENSION (extension));
+ }
+
+ return FALSE;
+}
+
+static void
+gdm_password_extension_queue_message (GdmLoginExtension *login_extension,
+ GdmServiceMessageType type,
+ const char *text)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+
+ QueuedMessage *message = g_slice_new (QueuedMessage);
+
+ message->text = g_strdup (text);
+ message->type = type;
+
+ g_queue_push_tail (extension->priv->message_queue, message);
+
+ if (extension->priv->message_timeout_id == 0) {
+ dequeue_message (extension);
+ }
+}
+
+static void
+gdm_password_extension_ask_question (GdmLoginExtension *login_extension,
+ const char *message)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ 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;
+
+ gtk_action_set_sensitive (extension->priv->login_action, TRUE);
+}
+
+static void
+gdm_password_extension_ask_secret (GdmLoginExtension *login_extension,
+ const char *message)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ 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;
+
+ gtk_action_set_sensitive (extension->priv->login_action, TRUE);
+}
+
+static void
+gdm_password_extension_reset (GdmLoginExtension *login_extension)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ 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;
+
+ set_message (extension, "");
+ purge_message_queue (extension);
+
+ gdm_login_extension_set_enabled (login_extension, FALSE);
+}
+
+static void
+gdm_password_extension_set_ready (GdmLoginExtension *extension)
+{
+ gdm_login_extension_set_enabled (extension, TRUE);
+}
+
+static char *
+gdm_password_extension_get_service_name (GdmLoginExtension *extension)
+{
+ return g_strdup (GDM_PASSWORD_EXTENSION_SERVICE_NAME);
+}
+
+static GtkWidget *
+gdm_password_extension_get_page (GdmLoginExtension *login_extension)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ return extension->priv->page;
+}
+
+static GtkActionGroup *
+gdm_password_extension_get_actions (GdmLoginExtension *login_extension)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ return g_object_ref (extension->priv->actions);
+}
+
+static void
+request_answer (GdmPasswordExtension *extension)
+{
+ const char *text;
+
+ if (!extension->priv->answer_pending) {
+ _gdm_login_extension_emit_answer (GDM_LOGIN_EXTENSION (extension), NULL);
+ return;
+ }
+
+ extension->priv->answer_pending = FALSE;
+ text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+ _gdm_login_extension_emit_answer (GDM_LOGIN_EXTENSION (extension), 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), "");
+}
+
+static gboolean
+gdm_password_extension_focus (GdmLoginExtension *login_extension)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ if (!extension->priv->answer_pending) {
+ _gdm_login_extension_emit_answer (login_extension, NULL);
+ return FALSE;
+ }
+
+ gtk_widget_grab_focus (extension->priv->prompt_entry);
+ return TRUE;
+}
+
+static gboolean
+gdm_password_extension_has_queued_messages (GdmLoginExtension *login_extension)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+
+ if (extension->priv->message_timeout_id != 0) {
+ return TRUE;
+ }
+
+ if (!g_queue_is_empty (extension->priv->message_queue)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GIcon *
+gdm_password_extension_get_icon (GdmLoginExtension *login_extension)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (login_extension);
+ return g_object_ref (extension->priv->icon);
+}
+
+static char *
+gdm_password_extension_get_name (GdmLoginExtension *login_extension)
+{
+ return g_strdup (_("Password Authentication"));
+}
+
+static char *
+gdm_password_extension_get_description (GdmLoginExtension *login_extension)
+{
+ return g_strdup (_("Log into session with username and password"));
+}
+
+static gboolean
+gdm_password_extension_is_choosable (GdmLoginExtension *login_extension)
+{
+ return FALSE;
+}
+
+static gboolean
+gdm_password_extension_is_visible (GdmLoginExtension *login_extension)
+{
+ return TRUE;
+}
+
+static void
+gdm_login_extension_iface_init (GdmLoginExtensionIface *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;
+ iface->is_visible = gdm_password_extension_is_visible;
+ iface->queue_message = gdm_password_extension_queue_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->focus = gdm_password_extension_focus;
+ iface->has_queued_messages = gdm_password_extension_has_queued_messages;
+}
+
+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)
+{
+ GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (object);
+
+ purge_message_queue (extension);
+}
+
+static void
+on_activate_log_in (GdmPasswordExtension *extension,
+ GtkAction *action)
+{
+ request_answer (extension);
+ gtk_action_set_sensitive (action, FALSE);
+}
+
+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_NAME);
+
+ action = gtk_action_new (GDM_LOGIN_EXTENSION_DEFAULT_ACTION,
+ _("Log In"), NULL, NULL);
+ g_signal_connect_swapped (action, "activate",
+ G_CALLBACK (on_activate_log_in), extension);
+ g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL);
+ gtk_action_group_add_action (extension->priv->actions,
+ action);
+
+ extension->priv->login_action = 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);
+
+ extension->priv->message_queue = g_queue_new ();
+
+ gdm_password_extension_reset (GDM_LOGIN_EXTENSION (extension));
+}
+
+void
+g_io_module_load (GIOModule *module)
+{
+ g_io_extension_point_implement (GDM_LOGIN_EXTENSION_POINT_NAME,
+ GDM_TYPE_PASSWORD_EXTENSION,
+ GDM_PASSWORD_EXTENSION_NAME,
+ G_MAXINT);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
diff --git a/gui/simple-greeter/extensions/password/gdm-password-extension.h b/gui/simple-greeter/extensions/password/gdm-password-extension.h
new file mode 100644
index 00000000..2b0a701e
--- /dev/null
+++ b/gui/simple-greeter/extensions/password/gdm-password-extension.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_PASSWORD_EXTENSION_H
+#define __GDM_PASSWORD_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-login-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))
+
+#define GDM_PASSWORD_EXTENSION_NAME "gdm-password-extension"
+
+typedef struct _GdmPasswordExtensionPrivate GdmPasswordExtensionPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmPasswordExtensionPrivate *priv;
+} GdmPasswordExtension;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GdmPasswordExtensionClass;
+
+GType gdm_password_extension_get_type (void);
+
+G_END_DECLS
+
+#endif /* GDM_PASSWORD_EXTENSION_H */
diff --git a/gui/simple-greeter/extensions/password/gdm-password.pam b/gui/simple-greeter/extensions/password/gdm-password.pam
new file mode 100644
index 00000000..bac431d3
--- /dev/null
+++ b/gui/simple-greeter/extensions/password/gdm-password.pam
@@ -0,0 +1,19 @@
+# Sample PAM file for doing password authentication.
+# Distros should replace this with what makes sense for them.
+auth required pam_env.so
+auth sufficient pam_unix.so nullok try_first_pass
+auth requisite pam_succeed_if.so uid >= 500 quiet
+auth required pam_deny.so
+
+account required pam_unix.so
+account sufficient pam_localuser.so
+account sufficient pam_succeed_if.so uid < 500 quiet
+account required pam_permit.so
+
+password requisite pam_cracklib.so try_first_pass retry=3 type=
+password sufficient pam_unix.so nullok try_first_pass use_authtok
+password required pam_deny.so
+
+session optional pam_keyinit.so revoke
+session required pam_limits.so
+session required pam_unix.so
diff --git a/gui/simple-greeter/extensions/password/page.ui b/gui/simple-greeter/extensions/password/page.ui
new file mode 100644
index 00000000..8fa5c7be
--- /dev/null
+++ b/gui/simple-greeter/extensions/password/page.ui
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <object class="GtkVBox" id="page">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="auth-input-box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="auth-prompt-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="auth-prompt-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="auth-message-box">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="auth-message-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/gui/simple-greeter/extensions/unified/Makefile.am b/gui/simple-greeter/extensions/unified/Makefile.am
new file mode 100644
index 00000000..31fa7442
--- /dev/null
+++ b/gui/simple-greeter/extensions/unified/Makefile.am
@@ -0,0 +1,42 @@
+NULL =
+PAM_SERVICE_NAME = gdm
+
+extensiondir = $(GDM_SIMPLE_GREETER_EXTENSIONS_DATA_DIR)/unified
+extension_DATA = page.ui
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter \
+ -DDMCONFDIR=\""$(dmconfdir)"\" \
+ -DGDMCONFDIR=\"$(gdmconfdir)\" \
+ -DPLUGINDATADIR=\""$(extensiondir)"\" \
+ -DGDM_UNIFIED_EXTENSION_SERVICE_NAME=\""$(PAM_SERVICE_NAME)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DSBINDIR=\""$(sbindir)"\" \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(SIMPLE_GREETER_CFLAGS) \
+ $(POLKIT_GNOME_CFLAGS) \
+ $(NULL)
+
+noinst_LTLIBRARIES = libunified.la
+
+libunified_la_CFLAGS = \
+ $(SIMPLE_GREETER_CFLAGS) \
+ $(NULL)
+
+libunified_la_LDFLAGS = -export-dynamic
+libunified_la_LIBADD = ../../../../common/libgdmcommon.la \
+ ../../libgdmsimplegreeter/libgdmsimplegreeter.la
+libunified_la_SOURCES = \
+ gdm-unified-extension.h \
+ gdm-unified-extension.c
+
+EXTRA_DIST = $(extension_DATA)
+
+MAINTAINERCLEANFILES = \
+ *~ \
+ Makefile.in
diff --git a/gui/simple-greeter/extensions/unified/gdm-unified-extension.c b/gui/simple-greeter/extensions/unified/gdm-unified-extension.c
new file mode 100644
index 00000000..785192ca
--- /dev/null
+++ b/gui/simple-greeter/extensions/unified/gdm-unified-extension.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-unified-extension.h"
+#include "gdm-login-extension.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+struct _GdmUnifiedExtensionPrivate
+{
+ GIcon *icon;
+ GtkWidget *page;
+ GtkActionGroup *actions;
+ GtkAction *login_action;
+
+ GtkWidget *message_label;
+ GtkWidget *prompt_label;
+ GtkWidget *prompt_entry;
+
+ GQueue *message_queue;
+ guint message_timeout_id;
+
+ guint answer_pending : 1;
+};
+
+typedef struct {
+ char *text;
+ GdmServiceMessageType type;
+} QueuedMessage;
+
+static void gdm_unified_extension_finalize (GObject *object);
+
+static void gdm_login_extension_iface_init (GdmLoginExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmUnifiedExtension,
+ gdm_unified_extension,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GDM_TYPE_LOGIN_EXTENSION,
+ gdm_login_extension_iface_init));
+
+static void
+set_message (GdmUnifiedExtension *extension,
+ const char *message)
+{
+ gtk_widget_show (extension->priv->message_label);
+ gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+free_queued_message (QueuedMessage *message)
+{
+ g_free (message->text);
+ g_slice_free (QueuedMessage, message);
+}
+
+static void
+purge_message_queue (GdmUnifiedExtension *extension)
+{
+ if (extension->priv->message_timeout_id) {
+ g_source_remove (extension->priv->message_timeout_id);
+ extension->priv->message_timeout_id = 0;
+ }
+ g_queue_foreach (extension->priv->message_queue,
+ (GFunc) free_queued_message,
+ NULL);
+ g_queue_clear (extension->priv->message_queue);
+}
+
+static gboolean
+dequeue_message (GdmUnifiedExtension *extension)
+{
+ if (!g_queue_is_empty (extension->priv->message_queue)) {
+ int duration;
+ gboolean needs_beep;
+
+ QueuedMessage *message;
+ message = (QueuedMessage *) g_queue_pop_head (extension->priv->message_queue);
+
+ switch (message->type) {
+ case GDM_SERVICE_MESSAGE_TYPE_INFO:
+ needs_beep = FALSE;
+ break;
+ case GDM_SERVICE_MESSAGE_TYPE_PROBLEM:
+ needs_beep = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ set_message (extension, message->text);
+
+ duration = (int) (g_utf8_strlen (message->text, -1) / 66.0) * 1000;
+ duration = CLAMP (duration, 400, 3000);
+
+ extension->priv->message_timeout_id = g_timeout_add (duration,
+ (GSourceFunc) dequeue_message,
+ extension);
+ if (needs_beep) {
+ gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (extension->priv->page)));
+ }
+
+ free_queued_message (message);
+ } else {
+ extension->priv->message_timeout_id = 0;
+
+ _gdm_login_extension_emit_message_queue_empty (GDM_LOGIN_EXTENSION (extension));
+ }
+
+ return FALSE;
+}
+
+static void
+gdm_unified_extension_queue_message (GdmLoginExtension *login_extension,
+ GdmServiceMessageType type,
+ const char *text)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+
+ QueuedMessage *message = g_slice_new (QueuedMessage);
+
+ message->text = g_strdup (text);
+ message->type = type;
+
+ g_queue_push_tail (extension->priv->message_queue, message);
+
+ if (extension->priv->message_timeout_id == 0) {
+ dequeue_message (extension);
+ }
+}
+
+static void
+gdm_unified_extension_ask_question (GdmLoginExtension *login_extension,
+ const char *message)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ 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;
+
+ gtk_action_set_sensitive (extension->priv->login_action, TRUE);
+}
+
+static void
+gdm_unified_extension_ask_secret (GdmLoginExtension *login_extension,
+ const char *message)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ 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;
+
+ gtk_action_set_sensitive (extension->priv->login_action, TRUE);
+}
+
+static void
+gdm_unified_extension_reset (GdmLoginExtension *login_extension)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ 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;
+
+ set_message (extension, "");
+ purge_message_queue (extension);
+
+ gdm_login_extension_set_enabled (login_extension, FALSE);
+}
+
+static void
+gdm_unified_extension_set_ready (GdmLoginExtension *extension)
+{
+ gdm_login_extension_set_enabled (extension, TRUE);
+}
+
+static char *
+gdm_unified_extension_get_service_name (GdmLoginExtension *extension)
+{
+ return g_strdup (GDM_UNIFIED_EXTENSION_SERVICE_NAME);
+}
+
+static GtkWidget *
+gdm_unified_extension_get_page (GdmLoginExtension *login_extension)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ return extension->priv->page;
+}
+
+static GtkActionGroup *
+gdm_unified_extension_get_actions (GdmLoginExtension *login_extension)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ return g_object_ref (extension->priv->actions);
+}
+
+static void
+request_answer (GdmUnifiedExtension *extension)
+{
+ const char *text;
+
+ if (!extension->priv->answer_pending) {
+ _gdm_login_extension_emit_answer (GDM_LOGIN_EXTENSION (extension), NULL);
+ return;
+ }
+
+ extension->priv->answer_pending = FALSE;
+ text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+ _gdm_login_extension_emit_answer (GDM_LOGIN_EXTENSION (extension), 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), "");
+}
+
+static gboolean
+gdm_unified_extension_focus (GdmLoginExtension *login_extension)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ if (!extension->priv->answer_pending) {
+ _gdm_login_extension_emit_answer (login_extension, NULL);
+ return FALSE;
+ }
+
+ gtk_widget_grab_focus (extension->priv->prompt_entry);
+ return TRUE;
+}
+
+static gboolean
+gdm_unified_extension_has_queued_messages (GdmLoginExtension *login_extension)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+
+ if (extension->priv->message_timeout_id != 0) {
+ return TRUE;
+ }
+
+ if (!g_queue_is_empty (extension->priv->message_queue)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GIcon *
+gdm_unified_extension_get_icon (GdmLoginExtension *login_extension)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (login_extension);
+ return g_object_ref (extension->priv->icon);
+}
+
+static char *
+gdm_unified_extension_get_name (GdmLoginExtension *login_extension)
+{
+ return g_strdup (_("Unified Authentication"));
+}
+
+static char *
+gdm_unified_extension_get_description (GdmLoginExtension *login_extension)
+{
+ return g_strdup (_("Log into session with username and unified"));
+}
+
+static gboolean
+gdm_unified_extension_is_choosable (GdmLoginExtension *login_extension)
+{
+ return FALSE;
+}
+
+static gboolean
+gdm_unified_extension_is_visible (GdmLoginExtension *login_extension)
+{
+ return TRUE;
+}
+
+static void
+gdm_login_extension_iface_init (GdmLoginExtensionIface *iface)
+{
+ iface->get_icon = gdm_unified_extension_get_icon;
+ iface->get_description = gdm_unified_extension_get_description;
+ iface->get_name = gdm_unified_extension_get_name;
+ iface->is_choosable = gdm_unified_extension_is_choosable;
+ iface->is_visible = gdm_unified_extension_is_visible;
+ iface->queue_message = gdm_unified_extension_queue_message;
+ iface->ask_question = gdm_unified_extension_ask_question;
+ iface->ask_secret = gdm_unified_extension_ask_secret;
+ iface->reset = gdm_unified_extension_reset;
+ iface->set_ready = gdm_unified_extension_set_ready;
+ iface->get_service_name = gdm_unified_extension_get_service_name;
+ iface->get_page = gdm_unified_extension_get_page;
+ iface->get_actions = gdm_unified_extension_get_actions;
+ iface->focus = gdm_unified_extension_focus;
+ iface->has_queued_messages = gdm_unified_extension_has_queued_messages;
+}
+
+static void
+gdm_unified_extension_class_init (GdmUnifiedExtensionClass *extension_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (extension_class);
+
+ object_class->finalize = gdm_unified_extension_finalize;
+
+ g_type_class_add_private (extension_class,
+ sizeof (GdmUnifiedExtensionPrivate));
+}
+
+static void
+gdm_unified_extension_finalize (GObject *object)
+{
+ GdmUnifiedExtension *extension = GDM_UNIFIED_EXTENSION (object);
+
+ purge_message_queue (extension);
+}
+
+static void
+on_activate_log_in (GdmUnifiedExtension *extension,
+ GtkAction *action)
+{
+ request_answer (extension);
+ gtk_action_set_sensitive (action, FALSE);
+}
+
+static void
+create_page (GdmUnifiedExtension *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 (GdmUnifiedExtension *extension)
+{
+ GtkAction *action;
+
+ extension->priv->actions = gtk_action_group_new (GDM_UNIFIED_EXTENSION_NAME);
+
+ action = gtk_action_new (GDM_LOGIN_EXTENSION_DEFAULT_ACTION,
+ _("Log In"), NULL, NULL);
+ g_signal_connect_swapped (action, "activate",
+ G_CALLBACK (on_activate_log_in), extension);
+ g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL);
+ gtk_action_group_add_action (extension->priv->actions,
+ action);
+
+ extension->priv->login_action = action;
+}
+
+static void
+gdm_unified_extension_init (GdmUnifiedExtension *extension)
+{
+ extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension,
+ GDM_TYPE_UNIFIED_EXTENSION,
+ GdmUnifiedExtensionPrivate);
+
+ extension->priv->icon = g_themed_icon_new ("dialog-unified");
+ create_page (extension);
+ create_actions (extension);
+
+ extension->priv->message_queue = g_queue_new ();
+
+ gdm_unified_extension_reset (GDM_LOGIN_EXTENSION (extension));
+}
+
+void
+gdm_unified_extension_load (void)
+{
+ g_io_extension_point_implement (GDM_LOGIN_EXTENSION_POINT_NAME,
+ GDM_TYPE_UNIFIED_EXTENSION,
+ GDM_UNIFIED_EXTENSION_NAME,
+ G_MAXINT);
+}
diff --git a/gui/simple-greeter/extensions/unified/gdm-unified-extension.h b/gui/simple-greeter/extensions/unified/gdm-unified-extension.h
new file mode 100644
index 00000000..0080bb46
--- /dev/null
+++ b/gui/simple-greeter/extensions/unified/gdm-unified-extension.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_UNIFIED_EXTENSION_H
+#define __GDM_UNIFIED_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-login-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_UNIFIED_EXTENSION (gdm_unified_extension_get_type ())
+#define GDM_UNIFIED_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_UNIFIED_EXTENSION, GdmUnifiedExtension))
+#define GDM_UNIFIED_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_UNIFIED_EXTENSION, GdmUnifiedExtensionClass))
+#define GDM_IS_UNIFIED_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_UNIFIED_EXTENSION))
+#define GDM_IS_UNIFIED_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_UNIFIED_EXTENSION))
+#define GDM_UNIFIED_EXTENSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_UNIFIED_EXTENSION, GdmUnifiedExtensionClass))
+
+#define GDM_UNIFIED_EXTENSION_NAME "gdm-unified-extension"
+
+typedef struct _GdmUnifiedExtensionPrivate GdmUnifiedExtensionPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmUnifiedExtensionPrivate *priv;
+} GdmUnifiedExtension;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GdmUnifiedExtensionClass;
+
+GType gdm_unified_extension_get_type (void);
+void gdm_unified_extension_load (void);
+
+G_END_DECLS
+
+#endif /* GDM_UNIFIED_EXTENSION_H */
diff --git a/gui/simple-greeter/extensions/unified/gdm.pam b/gui/simple-greeter/extensions/unified/gdm.pam
new file mode 100644
index 00000000..58c397d9
--- /dev/null
+++ b/gui/simple-greeter/extensions/unified/gdm.pam
@@ -0,0 +1,12 @@
+#%PAM-1.0
+auth required pam_env.so
+auth required pam_succeed_if.so user != root quiet
+auth sufficient pam_succeed_if.so user ingroup nopasswdlogin
+auth include system-auth
+account required pam_nologin.so
+account include system-auth
+password include system-auth
+session optional pam_keyinit.so force revoke
+session include system-auth
+session required pam_loginuid.so
+session optional pam_console.so
diff --git a/gui/simple-greeter/extensions/unified/page.ui b/gui/simple-greeter/extensions/unified/page.ui
new file mode 100644
index 00000000..8fa5c7be
--- /dev/null
+++ b/gui/simple-greeter/extensions/unified/page.ui
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <object class="GtkVBox" id="page">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="auth-input-box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="auth-prompt-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="auth-prompt-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="auth-message-box">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="auth-message-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/gui/simple-greeter/gdm-extension-list.c b/gui/simple-greeter/gdm-extension-list.c
new file mode 100644
index 00000000..148d0dd5
--- /dev/null
+++ b/gui/simple-greeter/gdm-extension-list.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-extension-list.h"
+
+#define GDM_EXTENSION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_EXTENSION_LIST, GdmExtensionListPrivate))
+typedef gboolean (* GdmExtensionListForeachFunc) (GdmExtensionList *extension_list,
+ GdmLoginExtension *extension,
+ gpointer data);
+
+
+struct GdmExtensionListPrivate
+{
+ GtkWidget *box;
+ GList *extensions;
+};
+
+enum {
+ ACTIVATED = 0,
+ DEACTIVATED,
+ NUMBER_OF_SIGNALS
+};
+
+static guint signals[NUMBER_OF_SIGNALS];
+
+static void gdm_extension_list_class_init (GdmExtensionListClass *klass);
+static void gdm_extension_list_init (GdmExtensionList *extension_list);
+static void gdm_extension_list_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmExtensionList, gdm_extension_list, GTK_TYPE_ALIGNMENT);
+
+static void
+on_extension_toggled (GdmExtensionList *widget,
+ GtkRadioButton *button)
+{
+ GdmLoginExtension *extension;
+
+ extension = g_object_get_data (G_OBJECT (button), "gdm-extension");
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+
+ GList *extension_node;
+ /* Sort the list such that the extensions the user clicks last end
+ * up first. This doesn't change the order in which the extensions
+ * appear in the UI, but will affect which extensions we implicitly
+ * activate if the currently active extension gets disabled.
+ */
+ extension_node = g_list_find (widget->priv->extensions, extension);
+ if (extension_node != NULL) {
+ widget->priv->extensions = g_list_delete_link (widget->priv->extensions, extension_node);
+ widget->priv->extensions = g_list_prepend (widget->priv->extensions,
+ extension);
+ }
+
+ g_signal_emit (widget, signals[ACTIVATED], 0, extension);
+ } else {
+ g_signal_emit (widget, signals[DEACTIVATED], 0, extension);
+ }
+}
+
+static GdmLoginExtension *
+gdm_extension_list_foreach_extension (GdmExtensionList *extension_list,
+ GdmExtensionListForeachFunc search_func,
+ gpointer data)
+{
+ GList *node;
+
+ for (node = extension_list->priv->extensions; node != NULL; node = node->next) {
+ GdmLoginExtension *extension;
+
+ extension = node->data;
+
+ if (search_func (extension_list, extension, data)) {
+ return g_object_ref (extension);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+on_extension_enabled (GdmExtensionList *extension_list,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *button;
+
+ button = g_object_get_data (G_OBJECT (extension), "gdm-extension-list-button");
+
+ gtk_widget_set_sensitive (button, TRUE);
+}
+
+static gboolean
+gdm_extension_list_set_active_extension (GdmExtensionList *widget,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *button;
+ gboolean was_sensitive;
+ gboolean was_activated;
+
+ if (!gdm_login_extension_is_visible (extension)) {
+ return FALSE;
+ }
+
+ was_sensitive = gtk_widget_get_sensitive (GTK_WIDGET (widget));
+ gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
+
+ button = GTK_WIDGET (g_object_get_data (G_OBJECT (extension),
+ "gdm-extension-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;
+}
+
+static void
+activate_first_available_extension (GdmExtensionList *extension_list)
+{
+ GList *node;
+
+ node = extension_list->priv->extensions;
+ while (node != NULL) {
+ GdmLoginExtension *extension;
+
+ extension = GDM_LOGIN_EXTENSION (node->data);
+
+ if (gdm_extension_list_set_active_extension (extension_list, extension)) {
+ break;
+ }
+
+ node = node->next;
+ }
+}
+
+static void
+on_extension_disabled (GdmExtensionList *extension_list,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *button;
+ gboolean was_active;
+
+ button = g_object_get_data (G_OBJECT (extension), "gdm-extension-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_extension (extension_list);
+ }
+}
+
+void
+gdm_extension_list_add_extension (GdmExtensionList *extension_list,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *image;
+ GtkWidget *button;
+ GIcon *icon;
+ char *description;
+
+ if (extension_list->priv->extensions == NULL) {
+ button = gtk_radio_button_new (NULL);
+ } else {
+ GdmLoginExtension *previous_extension;
+ GtkRadioButton *previous_button;
+
+ previous_extension = GDM_LOGIN_EXTENSION (extension_list->priv->extensions->data);
+ previous_button = GTK_RADIO_BUTTON (g_object_get_data (G_OBJECT (previous_extension), "gdm-extension-list-button"));
+ button = gtk_radio_button_new_from_widget (previous_button);
+ }
+ g_object_set_data (G_OBJECT (extension), "gdm-extension-list-button", button);
+
+ g_object_set (G_OBJECT (button), "draw-indicator", FALSE, NULL);
+ g_object_set_data (G_OBJECT (button), "gdm-extension", extension);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (on_extension_toggled),
+ extension_list);
+
+ gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+ gtk_widget_set_sensitive (button, gdm_login_extension_is_enabled (extension));
+
+ g_signal_connect_swapped (G_OBJECT (extension), "enabled",
+ G_CALLBACK (on_extension_enabled),
+ extension_list);
+
+ g_signal_connect_swapped (G_OBJECT (extension), "disabled",
+ G_CALLBACK (on_extension_disabled),
+ extension_list);
+
+ icon = gdm_login_extension_get_icon (extension);
+ 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_login_extension_get_description (extension);
+ gtk_widget_set_tooltip_text (button, description);
+ g_free (description);
+ gtk_widget_show (button);
+
+ gtk_container_add (GTK_CONTAINER (extension_list->priv->box), button);
+ extension_list->priv->extensions = g_list_append (extension_list->priv->extensions,
+ g_object_ref (extension));
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+ g_signal_emit (extension_list, signals[ACTIVATED], 0, extension);
+ }
+}
+
+void
+gdm_extension_list_remove_extension (GdmExtensionList *extension_list,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *button;
+ GList *node;
+
+ node = g_list_find (extension_list->priv->extensions, extension);
+
+ if (node == NULL) {
+ return;
+ }
+
+ extension_list->priv->extensions = g_list_delete_link (extension_list->priv->extensions, node);
+
+ button = g_object_get_data (G_OBJECT (extension), "gdm-extension-list-button");
+
+ if (button != NULL) {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (extension),
+ G_CALLBACK (on_extension_enabled),
+ extension_list);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (extension),
+ G_CALLBACK (on_extension_disabled),
+ extension_list);
+ gtk_widget_destroy (button);
+ g_object_set_data (G_OBJECT (extension), "gdm-extension-list-button", NULL);
+ }
+
+ g_object_unref (extension);
+
+ activate_first_available_extension (extension_list);
+}
+
+static void
+gdm_extension_list_class_init (GdmExtensionListClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdm_extension_list_finalize;
+
+ signals [ACTIVATED] = g_signal_new ("activated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmExtensionListClass, 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 (GdmExtensionListClass, deactivated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ g_type_class_add_private (klass, sizeof (GdmExtensionListPrivate));
+}
+
+static void
+gdm_extension_list_init (GdmExtensionList *widget)
+{
+ widget->priv = GDM_EXTENSION_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_extension_list_finalize (GObject *object)
+{
+ GdmExtensionList *widget;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_EXTENSION_LIST (object));
+
+ widget = GDM_EXTENSION_LIST (object);
+
+ g_list_foreach (widget->priv->extensions, (GFunc) g_object_unref, NULL);
+ g_list_free (widget->priv->extensions);
+
+ G_OBJECT_CLASS (gdm_extension_list_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_extension_list_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_EXTENSION_LIST, NULL);
+
+ return GTK_WIDGET (object);
+}
+
+static gboolean
+gdm_extension_list_extension_is_active (GdmExtensionList *extension_list,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *button;
+
+ button = g_object_get_data (G_OBJECT (extension), "gdm-extension-list-button");
+
+ return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+}
+
+GdmLoginExtension *
+gdm_extension_list_get_active_extension (GdmExtensionList *widget)
+{
+ return gdm_extension_list_foreach_extension (widget,
+ (GdmExtensionListForeachFunc)
+ gdm_extension_list_extension_is_active,
+ NULL);
+}
+
+int
+gdm_extension_list_get_number_of_visible_extensions (GdmExtensionList *widget)
+{
+ GList *node;
+ int number_of_visible_extensions;
+
+ number_of_visible_extensions = 0;
+ for (node = widget->priv->extensions; node != NULL; node = node->next) {
+ GdmLoginExtension *extension;
+
+ extension = node->data;
+
+ if (gdm_login_extension_is_enabled (extension) && gdm_login_extension_is_visible (extension)) {
+ number_of_visible_extensions++;
+ }
+ }
+
+ return number_of_visible_extensions;
+}
diff --git a/gui/simple-greeter/gdm-extension-list.h b/gui/simple-greeter/gdm-extension-list.h
new file mode 100644
index 00000000..acce3f8e
--- /dev/null
+++ b/gui/simple-greeter/gdm-extension-list.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_EXTENSION_LIST_H
+#define __GDM_EXTENSION_LIST_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-login-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_EXTENSION_LIST (gdm_extension_list_get_type ())
+#define GDM_EXTENSION_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_EXTENSION_LIST, GdmExtensionList))
+#define GDM_EXTENSION_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_EXTENSION_LIST, GdmExtensionListClass))
+#define GDM_IS_EXTENSION_LIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_EXTENSION_LIST))
+#define GDM_IS_EXTENSION_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_EXTENSION_LIST))
+#define GDM_EXTENSION_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_EXTENSION_LIST, GdmExtensionListClass))
+
+typedef struct GdmExtensionListPrivate GdmExtensionListPrivate;
+typedef struct _GdmExtensionList GdmExtensionList;
+
+struct _GdmExtensionList
+{
+ GtkAlignment parent;
+ GdmExtensionListPrivate *priv;
+};
+
+typedef struct
+{
+ GtkAlignmentClass parent_class;
+
+ void (* deactivated) (GdmExtensionList *widget,
+ GdmLoginExtension *extension);
+ void (* activated) (GdmExtensionList *widget,
+ GdmLoginExtension *extension);
+} GdmExtensionListClass;
+
+GType gdm_extension_list_get_type (void);
+GtkWidget * gdm_extension_list_new (void);
+
+GdmLoginExtension *gdm_extension_list_get_active_extension (GdmExtensionList *widget);
+void gdm_extension_list_add_extension (GdmExtensionList *widget,
+ GdmLoginExtension *extension);
+void gdm_extension_list_remove_extension (GdmExtensionList *widget,
+ GdmLoginExtension *extension);
+
+int gdm_extension_list_get_number_of_visible_extensions (GdmExtensionList *widget);
+G_END_DECLS
+
+#endif /* __GDM_EXTENSION_LIST_H */
diff --git a/gui/simple-greeter/gdm-greeter-client.c b/gui/simple-greeter/gdm-greeter-client.c
index 65d21d91..312b52c1 100644
--- a/gui/simple-greeter/gdm-greeter-client.c
+++ b/gui/simple-greeter/gdm-greeter-client.c
@@ -65,6 +65,7 @@ enum {
SECRET_INFO_QUERY,
SERVICE_UNAVAILABLE,
READY,
+ CONVERSATION_STOPPED,
RESET,
AUTHENTICATION_FAILED,
SELECTED_USER_CHANGED,
@@ -271,6 +272,13 @@ on_ready (GdmGreeterClient *client,
}
static void
+on_conversation_stopped (GdmGreeterClient *client,
+ DBusMessage *message)
+{
+ emit_string_signal_for_message (client, "ConversationStopped", message, CONVERSATION_STOPPED);
+}
+
+static void
on_reset (GdmGreeterClient *client,
DBusMessage *message)
{
@@ -742,6 +750,8 @@ client_dbus_handle_message (DBusConnection *connection,
on_service_unavailable (client, message);
} else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Ready")) {
on_ready (client, message);
+ } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "ConversationStopped")) {
+ 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, "AuthenticationFailed")) {
@@ -992,6 +1002,17 @@ gdm_greeter_client_class_init (GdmGreeterClientClass *klass)
G_TYPE_NONE,
1, G_TYPE_STRING);
+ gdm_greeter_client_signals[CONVERSATION_STOPPED] =
+ g_signal_new ("conversation-stopped",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmGreeterClientClass, conversation_stopped),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
gdm_greeter_client_signals[RESET] =
g_signal_new ("reset",
G_OBJECT_CLASS_TYPE (object_class),
diff --git a/gui/simple-greeter/gdm-greeter-client.h b/gui/simple-greeter/gdm-greeter-client.h
index 124f53d0..965035d7 100644
--- a/gui/simple-greeter/gdm-greeter-client.h
+++ b/gui/simple-greeter/gdm-greeter-client.h
@@ -63,6 +63,8 @@ typedef struct
const char *service_name);
void (* ready) (GdmGreeterClient *client,
const char *service_name);
+ void (* conversation_stopped) (GdmGreeterClient *client,
+ const char *service_name);
void (* reset) (GdmGreeterClient *client);
void (* authentication_failed) (GdmGreeterClient *client);
void (* selected_user_changed) (GdmGreeterClient *client,
diff --git a/gui/simple-greeter/gdm-greeter-login-window.c b/gui/simple-greeter/gdm-greeter-login-window.c
index efd4be02..ef220834 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.c
+++ b/gui/simple-greeter/gdm-greeter-login-window.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008, 2009 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
+ * Written by: William Jon McCann <mccann@jhu.edu>
+ * Ray Strode <rstrode@redhat.com>
+ *
*/
#include "config.h"
@@ -50,13 +53,19 @@
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
+#include "gdm-marshal.h"
+
#include "gdm-settings-client.h"
#include "gdm-settings-keys.h"
#include "gdm-profile.h"
+#include "gdm-greeter-client.h"
#include "gdm-greeter-login-window.h"
#include "gdm-user-chooser-widget.h"
#include "gdm-session-option-widget.h"
+#include "gdm-extension-list.h"
+
+#include "extensions/unified/gdm-unified-extension.h"
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -96,6 +105,7 @@ enum {
MODE_TIMED_LOGIN,
MODE_SELECTION,
MODE_AUTHENTICATION,
+ MODE_MULTIPLE_AUTHENTICATION,
};
enum {
@@ -109,19 +119,24 @@ struct GdmGreeterLoginWindowPrivate
GtkBuilder *builder;
GtkWidget *session_option_widget;
GtkWidget *user_chooser;
+ GtkWidget *extension_list;
GtkWidget *auth_banner_label;
GtkWidget *current_button;
-
+ GtkWidget *auth_page_box;
guint display_is_local : 1;
guint user_chooser_loaded : 1;
- gboolean session_ready_to_start : 1;
GConfClient *client;
+ GList *extensions;
+ GdmLoginExtension *active_extension;
+ GList *extensions_to_enable;
+ GList *extensions_to_stop;
gboolean banner_message_enabled;
guint gconf_cnxn;
guint last_mode;
guint dialog_mode;
+ guint next_mode;
gboolean user_list_disabled;
guint num_queries;
@@ -135,27 +150,16 @@ struct GdmGreeterLoginWindowPrivate
guint login_button_handler_id;
guint start_session_handler_id;
- GQueue *message_queue;
- guint message_timeout_id;
- guint message_queue_empty_reset_dialog_mode;
+ char *service_name_of_session_ready_to_start;
};
-typedef enum {
- QUEUE_MESSAGE_TYPE_INFO,
- QUEUE_MESSAGE_TYPE_PROBLEM
-} QueuedMessageType;
-
-typedef struct {
- char *text;
- QueuedMessageType type;
-} QueuedMessage;
-
enum {
PROP_0,
PROP_DISPLAY_IS_LOCAL,
};
enum {
+ START_CONVERSATION,
BEGIN_AUTO_LOGIN,
BEGIN_VERIFICATION,
BEGIN_VERIFICATION_FOR_USER,
@@ -182,6 +186,13 @@ static void switch_mode (GdmGreeterLoginWindow *login_window
static void update_banner_message (GdmGreeterLoginWindow *login_window);
static void reset_dialog (GdmGreeterLoginWindow *login_window,
guint dialog_mode);
+static void gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
+static void handle_stopped_conversation (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
+
+static void begin_single_service_verification (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
G_DEFINE_TYPE (GdmGreeterLoginWindow, gdm_greeter_login_window, GTK_TYPE_WINDOW)
@@ -207,9 +218,6 @@ set_sensitive (GdmGreeterLoginWindow *login_window,
{
GtkWidget *box;
- box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-input-box"));
- gtk_widget_set_sensitive (box, sensitive);
-
box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "buttonbox"));
gtk_widget_set_sensitive (box, sensitive);
@@ -219,117 +227,39 @@ set_sensitive (GdmGreeterLoginWindow *login_window,
static void
set_focus (GdmGreeterLoginWindow *login_window)
{
- GtkWidget *entry;
-
- entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
-
gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (login_window)), GDK_CURRENT_TIME);
- if (gtk_widget_get_realized (entry) && ! gtk_widget_has_focus (entry)) {
- gtk_widget_grab_focus (entry);
+ if (login_window->priv->active_extension != NULL &&
+ gdm_login_extension_focus (login_window->priv->active_extension)) {
+ char *name;
+ name = gdm_login_extension_get_name (login_window->priv->active_extension);
+ g_debug ("GdmGreeterLoginWindow: focusing extension %s", name);
+ g_free (name);
} else if (gtk_widget_get_realized (login_window->priv->user_chooser) && ! gtk_widget_has_focus (login_window->priv->user_chooser)) {
gtk_widget_grab_focus (login_window->priv->user_chooser);
}
-}
-
-static void
-set_message (GdmGreeterLoginWindow *login_window,
- const char *text)
-{
- GtkWidget *label;
-
- label = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-message-label"));
- gtk_label_set_text (GTK_LABEL (label), text);
-}
-
-static void
-free_queued_message (QueuedMessage *message)
-{
- g_free (message->text);
- g_slice_free (QueuedMessage, message);
-}
-
-static void
-purge_message_queue (GdmGreeterLoginWindow *login_window)
-{
- if (login_window->priv->message_timeout_id) {
- g_source_remove (login_window->priv->message_timeout_id);
- login_window->priv->message_timeout_id = 0;
- }
- g_queue_foreach (login_window->priv->message_queue,
- (GFunc) free_queued_message,
- NULL);
- g_queue_clear (login_window->priv->message_queue);
-
- login_window->priv->message_queue_empty_reset_dialog_mode = MODE_UNDEFINED;
}
static gboolean
-set_next_message_or_continue (GdmGreeterLoginWindow *login_window)
-{
- if (!g_queue_is_empty (login_window->priv->message_queue)) {
- int duration;
- gboolean needs_beep;
-
- QueuedMessage *message;
- message = (QueuedMessage *) g_queue_pop_head (login_window->priv->message_queue);
-
- switch (message->type) {
- case QUEUE_MESSAGE_TYPE_INFO:
- duration = INFO_MESSAGE_DURATION;
- needs_beep = FALSE;
- break;
- case QUEUE_MESSAGE_TYPE_PROBLEM:
- duration = PROBLEM_MESSAGE_DURATION;
- needs_beep = TRUE;
- break;
- default:
- g_assert_not_reached ();
- }
- set_message (login_window, message->text);
- login_window->priv->message_timeout_id = g_timeout_add_seconds (duration,
- (GSourceFunc) set_next_message_or_continue,
- login_window);
- if (needs_beep) {
- gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (login_window)));
- }
-
- free_queued_message (message);
- } else {
- set_message (login_window, "");
- login_window->priv->message_timeout_id = 0;
-
- if (login_window->priv->message_queue_empty_reset_dialog_mode != MODE_UNDEFINED) {
- /* All messages have been shown, reset */
- g_debug ("GdmGreeterLoginWindow: finally resetting dialog");
- reset_dialog (login_window,
- login_window->priv->message_queue_empty_reset_dialog_mode);
-
- } else if (login_window->priv->session_ready_to_start) {
- /* All messages have been shown, proceed */
- g_debug ("GdmGreeterLoginWindow: finally starting session");
- g_signal_emit (login_window, signals[START_SESSION], 0);
- }
- }
+queue_message_for_extension (GdmLoginExtension *extension,
+ const char *message)
+{
+ gdm_login_extension_queue_message (extension,
+ GDM_SERVICE_MESSAGE_TYPE_INFO,
+ message);
return FALSE;
}
static void
-queue_message (GdmGreeterLoginWindow *login_window,
- QueuedMessageType message_type,
- const char *text)
+set_message (GdmGreeterLoginWindow *login_window,
+ const char *text)
{
- QueuedMessage *message = g_slice_new (QueuedMessage);
-
- message->text = g_strdup (text);
- message->type = message_type;
-
- g_queue_push_tail (login_window->priv->message_queue, message);
+ g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window));
- if (login_window->priv->message_timeout_id == 0) {
- set_next_message_or_continue (login_window);
- }
+ g_list_foreach (login_window->priv->extensions,
+ (GFunc) queue_message_for_extension,
+ (gpointer) text);
}
static void
@@ -440,19 +370,64 @@ show_widget (GdmGreeterLoginWindow *login_window,
}
static void
-on_login_button_clicked_answer_query (GtkButton *button,
- GdmGreeterLoginWindow *login_window)
+hide_extension_actions (GdmLoginExtension *extension)
{
- GtkWidget *entry;
- const char *text;
+ GtkActionGroup *actions;
- set_busy (login_window);
- set_sensitive (login_window, FALSE);
+ actions = gdm_login_extension_get_actions (extension);
+
+ 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_extension (GdmLoginExtension *extension)
+{
+ GtkActionGroup *actions;
+ GtkAction *action;
+ GSList *proxies, *node;
- entry = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-prompt-entry"));
- text = gtk_entry_get_text (GTK_ENTRY (entry));
+ actions = gdm_login_extension_get_actions (extension);
+
+ if (actions == NULL) {
+ return;
+ }
+
+ action = gtk_action_group_get_action (actions, GDM_LOGIN_EXTENSION_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_get_can_default (widget) &&
+ gtk_widget_get_visible (widget)) {
+ gtk_widget_grab_default (widget);
+ break;
+ }
+ }
+}
+
+static void
+show_extension_actions (GdmLoginExtension *extension)
+{
+ GtkActionGroup *actions;
- g_signal_emit (login_window, signals[QUERY_ANSWER], 0, text);
+ actions = gdm_login_extension_get_actions (extension);
+ if (actions != NULL) {
+ gtk_action_group_set_sensitive (actions, TRUE);
+ gtk_action_group_set_visible (actions, TRUE);
+ g_object_unref (actions);
+ }
}
static void
@@ -515,13 +490,23 @@ set_log_in_button_mode (GdmGreeterLoginWindow *login_window,
login_window->priv->current_button = button;
+ g_list_foreach (login_window->priv->extensions, (GFunc) hide_extension_actions, NULL);
+
switch (mode) {
case LOGIN_BUTTON_HIDDEN:
+ if (login_window->priv->active_extension != NULL) {
+ hide_extension_actions (login_window->priv->active_extension);
+ }
+
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);
+ if (login_window->priv->active_extension != NULL) {
+ show_extension_actions (login_window->priv->active_extension);
+ grab_default_button_for_extension (login_window->priv->active_extension);
+ }
+
+ 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);
@@ -565,6 +550,7 @@ maybe_show_cancel_button (GdmGreeterLoginWindow *login_window)
show = TRUE;
break;
case MODE_AUTHENTICATION:
+ case MODE_MULTIPLE_AUTHENTICATION:
if (login_window->priv->num_queries > 1) {
/* if we are inside a pam conversation past
the first step */
@@ -585,6 +571,24 @@ maybe_show_cancel_button (GdmGreeterLoginWindow *login_window)
}
static void
+update_extension_list_visibility (GdmGreeterLoginWindow *login_window)
+{
+ int number_of_extensions;
+
+ if (login_window->priv->dialog_mode != MODE_MULTIPLE_AUTHENTICATION) {
+ gtk_widget_hide (login_window->priv->extension_list);
+ return;
+ }
+
+ number_of_extensions = gdm_extension_list_get_number_of_visible_extensions (GDM_EXTENSION_LIST (login_window->priv->extension_list));
+ if (number_of_extensions > 1) {
+ gtk_widget_show (login_window->priv->extension_list);
+ } else {
+ gtk_widget_hide (login_window->priv->extension_list);
+ }
+}
+
+static void
switch_mode (GdmGreeterLoginWindow *login_window,
int number)
{
@@ -601,6 +605,8 @@ switch_mode (GdmGreeterLoginWindow *login_window,
login_window->priv->dialog_mode = number;
}
+ login_window->priv->next_mode = MODE_UNDEFINED;
+
switch (number) {
case MODE_SELECTION:
set_log_in_button_mode (login_window, LOGIN_BUTTON_HIDDEN);
@@ -613,6 +619,7 @@ switch_mode (GdmGreeterLoginWindow *login_window,
gtk_widget_show (login_window->priv->session_option_widget);
break;
case MODE_AUTHENTICATION:
+ case MODE_MULTIPLE_AUTHENTICATION:
set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
set_sensitive (login_window, FALSE);
gtk_widget_show (login_window->priv->session_option_widget);
@@ -622,6 +629,7 @@ switch_mode (GdmGreeterLoginWindow *login_window,
}
show_widget (login_window, "auth-input-box", FALSE);
+ update_extension_list_visibility (login_window);
maybe_show_cancel_button (login_window);
/*
@@ -652,58 +660,72 @@ switch_mode (GdmGreeterLoginWindow *login_window,
}
}
-static void
-choose_user (GdmGreeterLoginWindow *login_window,
- const char *user_name)
+static GdmLoginExtension *
+find_extension_with_service_name (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
{
- guint mode;
+ GList *node;
- g_assert (user_name != NULL);
+ node = login_window->priv->extensions;
+ while (node != NULL) {
+ GdmLoginExtension *extension;
+ char *extension_service_name;
+ gboolean has_service_name;
- g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
- 0, user_name);
+ extension = GDM_LOGIN_EXTENSION (node->data);
- mode = MODE_AUTHENTICATION;
- 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);
+ extension_service_name = gdm_login_extension_get_service_name (extension);
+ has_service_name = strcmp (service_name, extension_service_name) == 0;
+ g_free (extension_service_name);
- login_window->priv->timed_login_enabled = TRUE;
- restart_timed_login_timeout (login_window);
+ if (has_service_name) {
+ return extension;
+ }
- /* just wait for the user to select language and stuff */
- mode = MODE_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);
+ node = node->next;
}
- switch_mode (login_window, mode);
+ return NULL;
}
-static void
-retry_login (GdmGreeterLoginWindow *login_window)
+static gboolean
+reset_extension (GdmLoginExtension *extension,
+ GdmGreeterLoginWindow *login_window)
{
- GtkWidget *entry;
- char *user_name;
+ char *name;
- user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser));
- if (user_name == NULL) {
- return;
- }
+ name = gdm_login_extension_get_name (extension);
+ g_debug ("Resetting extension '%s'", name);
+ g_free (name);
- g_debug ("GdmGreeterLoginWindow: Retrying login for %s", user_name);
+ login_window->priv->extensions_to_enable = g_list_remove (login_window->priv->extensions_to_enable, extension);
- entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
- gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
+ hide_extension_actions (extension);
+ gdm_extension_list_remove_extension (GDM_EXTENSION_LIST (login_window->priv->extension_list), extension);
+ gdm_login_extension_reset (extension);
+ return FALSE;
+}
- choose_user (login_window, user_name);
+static gboolean
+extensions_are_enabled (GdmGreeterLoginWindow *login_window)
+{
- g_free (user_name);
+ GList *node;
+
+ node = login_window->priv->extensions;
+ while (node != NULL) {
+ GdmLoginExtension *extension;
+
+ extension = GDM_LOGIN_EXTENSION (node->data);
+
+ if (!gdm_login_extension_is_enabled (extension)) {
+ return FALSE;
+ }
+
+ node = node->next;
+ }
+
+ return TRUE;
}
static gboolean
@@ -713,6 +735,12 @@ can_jump_to_authenticate (GdmGreeterLoginWindow *login_window)
if (!login_window->priv->user_chooser_loaded) {
res = FALSE;
+ } else if (!extensions_are_enabled (login_window)) {
+ res = FALSE;
+ } else if (login_window->priv->dialog_mode == MODE_AUTHENTICATION) {
+ res = FALSE;
+ } else if (login_window->priv->dialog_mode == MODE_MULTIPLE_AUTHENTICATION) {
+ res = FALSE;
} else if (login_window->priv->user_list_disabled) {
res = (login_window->priv->timed_login_username == NULL);
} else {
@@ -723,20 +751,93 @@ can_jump_to_authenticate (GdmGreeterLoginWindow *login_window)
}
static void
+begin_other_verification (GdmGreeterLoginWindow *login_window)
+{
+ /* FIXME: we should drop this code and do all OTHER handling
+ * entirely from within the password plugin
+ * (ala how smart card manages its "Smartcard Authentication" item)
+ */
+ begin_single_service_verification (login_window, "gdm-password");
+}
+
+static void
+set_extension_active (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *extension)
+{
+ GtkWidget *container;
+ char *name;
+
+ name = gdm_login_extension_get_name (extension);
+ g_debug ("GdmGreeterLoginWindow: extension '%s' activated", name);
+ g_free (name);
+
+ container = g_object_get_data (G_OBJECT (extension),
+ "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_login_extension_get_page (extension);
+ if (page != NULL) {
+ gtk_container_add (GTK_CONTAINER (container), page);
+ gtk_widget_show (page);
+ }
+ g_object_set_data (G_OBJECT (extension),
+ "gdm-greeter-login-window-page-container",
+ container);
+ }
+
+ gtk_widget_show (container);
+
+ login_window->priv->active_extension = extension;
+ switch_mode (login_window, login_window->priv->dialog_mode);
+}
+
+static void
+clear_active_extension (GdmGreeterLoginWindow *login_window)
+{
+
+ GtkWidget *container;
+ GtkActionGroup *actions;
+
+ if (login_window->priv->active_extension == NULL) {
+ return;
+ }
+
+ container = g_object_get_data (G_OBJECT (login_window->priv->active_extension),
+ "gdm-greeter-login-window-page-container");
+
+ if (container != NULL) {
+ gtk_widget_hide (container);
+ }
+
+ actions = gdm_login_extension_get_actions (login_window->priv->active_extension);
+
+ if (actions != NULL) {
+ gtk_action_group_set_sensitive (actions, FALSE);
+ gtk_action_group_set_visible (actions, FALSE);
+ g_object_unref (actions);
+ }
+
+ login_window->priv->active_extension = NULL;
+}
+
+static void
reset_dialog (GdmGreeterLoginWindow *login_window,
guint dialog_mode)
{
- GtkWidget *entry;
- GtkWidget *label;
-
g_debug ("GdmGreeterLoginWindow: Resetting dialog to mode %u", dialog_mode);
set_busy (login_window);
set_sensitive (login_window, FALSE);
login_window->priv->num_queries = 0;
- purge_message_queue (login_window);
- login_window->priv->session_ready_to_start = FALSE;
+ g_free (login_window->priv->service_name_of_session_ready_to_start);
+ login_window->priv->service_name_of_session_ready_to_start = NULL;
if (dialog_mode == MODE_SELECTION) {
if (login_window->priv->timed_login_enabled) {
@@ -760,28 +861,20 @@ reset_dialog (GdmGreeterLoginWindow *login_window,
set_message (login_window, "");
}
- entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
-
- gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
-
- gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE);
-
- label = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-label"));
- gtk_label_set_text (GTK_LABEL (label), "");
+ g_list_foreach (login_window->priv->extensions, (GFunc) reset_extension, login_window);
if (can_jump_to_authenticate (login_window)) {
/* If we don't have a user list jump straight to authenticate */
g_debug ("GdmGreeterLoginWindow: jumping straight to authenticate");
- switch_mode (login_window, MODE_AUTHENTICATION);
-
- g_debug ("Starting PAM conversation since no local users");
g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
0, GDM_USER_CHOOSER_USER_OTHER);
- g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
+ begin_other_verification (login_window);
} else {
+ clear_active_extension (login_window);
switch_mode (login_window, dialog_mode);
}
+ gtk_widget_set_sensitive (login_window->priv->extension_list, TRUE);
set_ready (login_window);
set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
update_banner_message (login_window);
@@ -792,23 +885,80 @@ reset_dialog (GdmGreeterLoginWindow *login_window,
}
static void
-do_cancel (GdmGreeterLoginWindow *login_window)
+restart_conversations (GdmGreeterLoginWindow *login_window)
{
- /* need to wait for response from backend */
- set_message (login_window, _("Cancelling…"));
set_busy (login_window);
set_sensitive (login_window, FALSE);
g_signal_emit (login_window, signals[CANCELLED], 0);
}
+static gboolean
+has_queued_messages (GdmGreeterLoginWindow *login_window)
+{
+ GList *node;
+
+ node = login_window->priv->extensions;
+ while (node != NULL) {
+ GdmLoginExtension *extension;
+
+ extension = (GdmLoginExtension *) node->data;
+
+ if (gdm_login_extension_has_queued_messages (extension)) {
+ return TRUE;
+ }
+ node = node->next;
+ }
+
+ return FALSE;
+}
+
+static void
+reset_dialog_after_messages (GdmGreeterLoginWindow *login_window,
+ guint dialog_mode)
+{
+ if (has_queued_messages (login_window)) {
+ g_debug ("GdmGreeterLoginWindow: will reset dialog after pending messages");
+ login_window->priv->next_mode = dialog_mode;
+ } else {
+ g_debug ("GdmGreeterLoginWindow: resetting dialog");
+ reset_dialog (login_window, dialog_mode);
+ }
+
+}
+
+static void
+do_cancel (GdmGreeterLoginWindow *login_window)
+{
+ /* need to wait for response from backend */
+ set_message (login_window, _("Cancelling…"));
+ restart_conversations (login_window);
+ reset_dialog_after_messages (login_window, MODE_SELECTION);
+}
+
gboolean
-gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
{
+ GdmLoginExtension *extension;
+
g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
- if (login_window->priv->message_queue_empty_reset_dialog_mode != MODE_UNDEFINED) {
- g_debug ("GdmGreeterLoginWindow: Ignoring daemon Ready event since still showing messages");
- return TRUE;
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension != NULL) {
+ if (!login_window->priv->user_chooser_loaded) {
+ g_debug ("GdmGreeterLoginWindow: Ignoring daemon Ready event since not loaded yet");
+ login_window->priv->extensions_to_enable = g_list_prepend (login_window->priv->extensions_to_enable,
+ extension);
+ return TRUE;
+ } else if (login_window->priv->next_mode != MODE_UNDEFINED) {
+ g_debug ("GdmGreeterLoginWindow: Ignoring daemon Ready event since still showing messages");
+ login_window->priv->extensions_to_enable = g_list_prepend (login_window->priv->extensions_to_enable,
+ extension);
+ return TRUE;
+ }
+
+ gdm_login_extension_set_ready (extension);
}
set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
@@ -816,86 +966,177 @@ gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window)
set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (login_window)));
- /* If we are retrying a previously selected user */
- if (!login_window->priv->user_list_disabled &&
- login_window->priv->dialog_mode == MODE_AUTHENTICATION) {
- retry_login (login_window);
- } else {
- /* If the user list is disabled, then start the PAM conversation */
- if (can_jump_to_authenticate (login_window)) {
- g_debug ("Starting PAM conversation since user list disabled");
- g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
- 0, GDM_USER_CHOOSER_USER_OTHER);
- g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
- }
+ /* If the user list is disabled, then start the PAM conversation */
+ if (can_jump_to_authenticate (login_window)) {
+ g_debug ("Starting PAM conversation since user list disabled or no local users");
+ g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+ 0, GDM_USER_CHOOSER_USER_OTHER);
+ begin_other_verification (login_window);
}
return TRUE;
}
static void
-reset_dialog_after_messages (GdmGreeterLoginWindow *login_window,
- guint dialog_mode)
+handle_stopped_conversation (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
{
- if (!g_queue_is_empty (login_window->priv->message_queue)) {
- g_debug ("GdmGreeterLoginWindow: will reset dialog after pending messages");
- login_window->priv->message_queue_empty_reset_dialog_mode = dialog_mode;
- } else {
- g_debug ("GdmGreeterLoginWindow: resetting dialog");
- reset_dialog (login_window, dialog_mode);
+ GdmLoginExtension *extension;
+
+ /* If the password conversation failed, then start over
+ *
+ * FIXME: we need to get this policy out of the source code
+ */
+ if (strcmp (service_name, "gdm-password") == 0) {
+ g_debug ("GdmGreeterLoginWindow: main conversation failed, starting over");
+ restart_conversations (login_window);
+ reset_dialog_after_messages (login_window, MODE_SELECTION);
+ return;
+ }
+
+ if (login_window->priv->dialog_mode == MODE_AUTHENTICATION) {
+ g_debug ("GdmGreeterLoginWindow: conversation failed, starting over");
+ restart_conversations (login_window);
+ reset_dialog_after_messages (login_window, MODE_AUTHENTICATION);
+ return;
+ } else if (login_window->priv->dialog_mode != MODE_MULTIPLE_AUTHENTICATION) {
+ g_warning ("conversation %s stopped when it shouldn't have been running (mode %d)",
+ service_name, login_window->priv->dialog_mode);
+ restart_conversations (login_window);
+ return;
+ }
+
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension != NULL) {
+ gdm_login_extension_reset (extension);
+
+ login_window->priv->extensions_to_stop = g_list_remove (login_window->priv->extensions_to_stop, extension);
+ }
+
+ /* If every conversation has failed, then just start over.
+ */
+ extension = gdm_extension_list_get_active_extension (GDM_EXTENSION_LIST (login_window->priv->extension_list));
+
+ if (extension == NULL || !gdm_login_extension_is_enabled (extension)) {
+ g_debug ("GdmGreeterLoginWindow: No conversations left, starting over");
+ restart_conversations (login_window);
+ reset_dialog_after_messages (login_window, MODE_SELECTION);
+ }
+
+ if (extension != NULL) {
+ g_object_unref (extension);
}
+ update_extension_list_visibility (login_window);
}
gboolean
-gdm_greeter_login_window_authentication_failed (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
{
+ GdmLoginExtension *extension;
+
g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
- g_debug ("GdmGreeterLoginWindow: got authentication failed");
- reset_dialog_after_messages (login_window, MODE_AUTHENTICATION);
+ g_debug ("GdmGreeterLoginWindow: conversation '%s' has stopped", service_name);
+
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension != NULL && gdm_login_extension_is_enabled (extension)) {
+ if (gdm_login_extension_has_queued_messages (extension)) {
+ login_window->priv->extensions_to_stop = g_list_prepend (login_window->priv->extensions_to_stop, extension);
+ } else {
+ handle_stopped_conversation (login_window, service_name);
+ }
+ }
+
return TRUE;
}
+static gboolean
+restart_extension_conversation (GdmLoginExtension *extension,
+ GdmGreeterLoginWindow *login_window)
+{
+ char *service_name;
+
+ login_window->priv->extensions_to_stop = g_list_remove (login_window->priv->extensions_to_stop, extension);
+
+ service_name = gdm_login_extension_get_service_name (extension);
+ if (service_name != NULL) {
+ char *name;
+
+ name = gdm_login_extension_get_name (extension);
+ 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);
- g_debug ("GdmGreeterLoginWindow: got reset");
reset_dialog_after_messages (login_window, MODE_SELECTION);
+ g_list_foreach (login_window->priv->extensions,
+ (GFunc) restart_extension_conversation,
+ login_window);
+
+ g_free (login_window->priv->service_name_of_session_ready_to_start);
+ login_window->priv->service_name_of_session_ready_to_start = NULL;
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);
+ GdmLoginExtension *extension;
+ g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
g_debug ("GdmGreeterLoginWindow: info: %s", text);
- queue_message (GDM_GREETER_LOGIN_WINDOW (login_window),
- QUEUE_MESSAGE_TYPE_INFO,
- text);
maybe_show_cancel_button (login_window);
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension != NULL) {
+ gdm_login_extension_queue_message (extension,
+ GDM_SERVICE_MESSAGE_TYPE_INFO,
+ text);
+ show_extension_actions (extension);
+ }
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);
+ GdmLoginExtension *extension;
+ g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
g_debug ("GdmGreeterLoginWindow: problem: %s", text);
maybe_show_cancel_button (login_window);
- queue_message (GDM_GREETER_LOGIN_WINDOW (login_window),
- QUEUE_MESSAGE_TYPE_PROBLEM,
- text);
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension != NULL) {
+ gdm_login_extension_queue_message (extension,
+ GDM_SERVICE_MESSAGE_TYPE_PROBLEM,
+ text);
+ show_extension_actions (extension);
+ }
return TRUE;
}
@@ -919,6 +1160,36 @@ request_timed_login (GdmGreeterLoginWindow *login_window)
login_window->priv->timed_login_already_enabled = TRUE;
}
+gboolean
+gdm_greeter_login_window_service_unavailable (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
+{
+ GdmLoginExtension *extension;
+
+ g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
+ g_debug ("GdmGreeterLoginWindow: service unavailable: %s", service_name);
+
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension != NULL) {
+ GdmLoginExtension *active_extension;
+
+ gdm_login_extension_set_enabled (extension, FALSE);
+
+ active_extension = gdm_extension_list_get_active_extension (GDM_EXTENSION_LIST (login_window->priv->extension_list));
+
+ if (active_extension == extension) {
+ restart_conversations (login_window);
+ }
+
+ if (active_extension != NULL) {
+ g_object_unref (active_extension);
+ }
+ }
+
+ return TRUE;
+}
+
void
gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_window,
const char *username,
@@ -946,21 +1217,40 @@ gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_windo
}
static void
-gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_start_session (GdmGreeterLoginWindow *login_window)
{
- login_window->priv->session_ready_to_start = TRUE;
+ g_debug ("GdmGreeterLoginWindow: starting session");
+ g_signal_emit (login_window,
+ signals[START_SESSION],
+ 0,
+ login_window->priv->service_name_of_session_ready_to_start);
+ g_free (login_window->priv->service_name_of_session_ready_to_start);
+ login_window->priv->service_name_of_session_ready_to_start = NULL;
+}
+
+static void
+gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
+{
+ GdmLoginExtension *extension;
+
+ extension = find_extension_with_service_name (login_window, service_name);
- if (login_window->priv->message_timeout_id == 0) {
- set_next_message_or_continue (login_window);
+ login_window->priv->service_name_of_session_ready_to_start = g_strdup (service_name);
+
+ if (!gdm_login_extension_has_queued_messages (extension)) {
+ g_debug ("GdmGreeterLoginWindow: starting session");
+ g_signal_emit (login_window, signals[START_SESSION], 0, service_name);
+ gdm_greeter_login_window_start_session (login_window);
}
}
gboolean
gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
const char *text)
{
- GtkWidget *entry;
- GtkWidget *label;
+ GdmLoginExtension *extension;
g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
@@ -969,16 +1259,13 @@ gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
g_debug ("GdmGreeterLoginWindow: info query: %s", text);
- entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
- gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
- gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE);
- set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
-
- label = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-label"));
- gtk_label_set_text (GTK_LABEL (label), text);
+ extension = find_extension_with_service_name (login_window, service_name);
- show_widget (login_window, "auth-input-box", TRUE);
+ if (extension != NULL) {
+ gdm_login_extension_ask_question (extension, text);
+ }
+ 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));
@@ -990,26 +1277,24 @@ gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
gboolean
gdm_greeter_login_window_secret_info_query (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
const char *text)
{
- GtkWidget *entry;
- GtkWidget *label;
+
+ GdmLoginExtension *extension;
g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
login_window->priv->num_queries++;
maybe_show_cancel_button (login_window);
- entry = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-entry"));
- gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
- gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
- set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
+ extension = find_extension_with_service_name (login_window, service_name);
- label = GTK_WIDGET (gtk_builder_get_object (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->builder, "auth-prompt-label"));
- gtk_label_set_text (GTK_LABEL (label), text);
+ if (extension != NULL) {
+ gdm_login_extension_ask_secret (extension, text);
+ }
- show_widget (login_window, "auth-input-box", TRUE);
- gtk_widget_show (login_window->priv->session_option_widget);
+ 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));
@@ -1020,13 +1305,16 @@ gdm_greeter_login_window_secret_info_query (GdmGreeterLoginWindow *login_window,
}
void
-gdm_greeter_login_window_session_opened (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_session_opened (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
{
g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window));
- g_debug ("GdmGreeterLoginWindow: session now opened");
+ g_debug ("GdmGreeterLoginWindow: session now opened 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
@@ -1090,6 +1378,51 @@ on_user_chooser_visibility_changed (GdmGreeterLoginWindow *login_window)
update_banner_message (login_window);
}
+static gboolean
+begin_extension_verification_for_selected_user (GdmLoginExtension *extension,
+ GdmGreeterLoginWindow *login_window)
+{
+ char *user_name;
+ char *service_name;
+
+ user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser));
+
+ if (user_name == NULL) {
+ return TRUE;
+ }
+
+ service_name = gdm_login_extension_get_service_name (extension);
+ if (service_name != NULL) {
+ g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, service_name, user_name);
+ g_free (service_name);
+ }
+
+ gdm_extension_list_add_extension (GDM_EXTENSION_LIST (login_window->priv->extension_list),
+ extension);
+
+ g_free (user_name);
+ return FALSE;
+}
+
+static void
+enable_waiting_extensions (GdmGreeterLoginWindow *login_window)
+{
+ GList *node;
+
+ node = login_window->priv->extensions_to_enable;
+ while (node != NULL) {
+ GdmLoginExtension *extension;
+
+ extension = GDM_LOGIN_EXTENSION (node->data);
+
+ gdm_login_extension_set_ready (extension);
+
+ node = node->next;
+ }
+
+ login_window->priv->extensions_to_enable = NULL;
+}
+
static void
on_users_loaded (GdmUserChooserWidget *user_chooser,
GdmGreeterLoginWindow *login_window)
@@ -1103,37 +1436,155 @@ on_users_loaded (GdmUserChooserWidget *user_chooser,
gtk_widget_show (login_window->priv->user_chooser);
}
+ enable_waiting_extensions (login_window);
+
if (login_window->priv->timed_login_username != NULL
&& !login_window->priv->timed_login_already_enabled) {
request_timed_login (login_window);
} else if (can_jump_to_authenticate (login_window)) {
+
/* jump straight to authenticate */
g_debug ("GdmGreeterLoginWindow: jumping straight to authenticate");
-
- switch_mode (login_window, MODE_AUTHENTICATION);
-
- g_debug ("Starting PAM conversation since no local users");
g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
0, GDM_USER_CHOOSER_USER_OTHER);
- g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
+ begin_other_verification (login_window);
+ }
+}
+
+static void
+choose_user (GdmGreeterLoginWindow *login_window,
+ const char *user_name)
+{
+ GdmLoginExtension *extension;
+
+ g_assert (user_name != NULL);
+ g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
+
+ g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+ 0, user_name);
+
+ g_list_foreach (login_window->priv->extensions,
+ (GFunc) begin_extension_verification_for_selected_user,
+ login_window);
+
+ extension = gdm_extension_list_get_active_extension (GDM_EXTENSION_LIST (login_window->priv->extension_list));
+ set_extension_active (login_window, extension);
+ g_object_unref (extension);
+
+ switch_mode (login_window, MODE_MULTIPLE_AUTHENTICATION);
+ update_extension_list_visibility (login_window);
+}
+
+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_message (login_window, _("Select language and click Log In"));
+
+ clear_active_extension (login_window);
+ switch_mode (login_window, MODE_TIMED_LOGIN);
+
+ show_widget (login_window, "conversation-list", FALSE);
+ g_list_foreach (login_window->priv->extensions,
+ (GFunc) reset_extension,
+ login_window);
+}
+
+static void
+reset_extension_if_not_given (GdmLoginExtension *extension,
+ GdmLoginExtension *given_extension)
+{
+ if (extension == given_extension) {
+ return;
+ }
+
+ gdm_login_extension_reset (extension);
+}
+
+static void
+reset_every_extension_but_given_extension (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *extension)
+{
+ g_list_foreach (login_window->priv->extensions,
+ (GFunc) reset_extension_if_not_given,
+ extension);
+
+}
+
+static void
+begin_single_service_verification (GdmGreeterLoginWindow *login_window,
+ const char *service_name)
+{
+ GdmLoginExtension *extension;
+
+ extension = find_extension_with_service_name (login_window, service_name);
+
+ if (extension == NULL) {
+ g_debug ("GdmGreeterLoginWindow: %s has no extension 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);
+
+ reset_every_extension_but_given_extension (login_window, extension);
+
+ set_extension_active (login_window, extension);
+ switch_mode (login_window, MODE_AUTHENTICATION);
+
+ show_widget (login_window, "conversation-list", FALSE);
}
static void
-on_user_chosen (GdmUserChooserWidget *user_chooser,
- GdmGreeterLoginWindow *login_window)
+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));
- g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
- if (user_name == NULL) {
+ if (user_name != NULL) {
+ g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
+ choose_user (login_window, user_name);
+ g_free (user_name);
return;
}
- choose_user (login_window, user_name);
- g_free (user_name);
+ item_id = gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (user_chooser));
+ g_debug ("GdmGreeterLoginWindow: item chosen '%s'", item_id);
+
+ g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+ 0, item_id);
+
+ if (strcmp (item_id, GDM_USER_CHOOSER_USER_OTHER) == 0) {
+ g_debug ("GdmGreeterLoginWindow: Starting all auth conversations");
+ g_free (item_id);
+
+ begin_other_verification (login_window);
+ } else if (strcmp (item_id, GDM_USER_CHOOSER_USER_GUEST) == 0) {
+ /* FIXME: handle guest account stuff */
+ g_free (item_id);
+ } 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 {
+ g_debug ("GdmGreeterLoginWindow: Starting single auth conversation");
+ begin_single_service_verification (login_window, item_id);
+ g_free (item_id);
+ }
}
static void
@@ -1354,12 +1805,40 @@ create_computer_info (GdmGreeterLoginWindow *login_window)
#define INVISIBLE_CHAR_BULLET 0x2022
#define INVISIBLE_CHAR_NONE 0
+static void
+on_extension_activated (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *extension)
+{
+ set_extension_active (login_window, extension);
+}
+
+static void
+on_extension_deactivated (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *extension)
+{
+ char *name;
+
+ if (login_window->priv->active_extension != extension) {
+ g_warning ("inactive extension has been deactivated");
+ return;
+ }
+
+ name = gdm_login_extension_get_name (extension);
+ g_debug ("GdmGreeterLoginWindow: extension '%s' now in background", name);
+ g_free (name);
+
+ clear_active_extension (login_window);
+
+ login_window->priv->active_extension = gdm_extension_list_get_active_extension (GDM_EXTENSION_LIST (login_window->priv->extension_list));
+ g_object_unref (login_window->priv->active_extension);
+}
static void
register_custom_types (GdmGreeterLoginWindow *login_window)
{
GType types[] = { GDM_TYPE_USER_CHOOSER_WIDGET,
- GDM_TYPE_SESSION_OPTION_WIDGET };
+ GDM_TYPE_SESSION_OPTION_WIDGET,
+ GDM_TYPE_EXTENSION_LIST };
int i;
for (i = 0; i < G_N_ELEMENTS (types); i++) {
@@ -1370,7 +1849,6 @@ register_custom_types (GdmGreeterLoginWindow *login_window)
static void
load_theme (GdmGreeterLoginWindow *login_window)
{
- GtkWidget *entry;
GtkWidget *button;
GtkWidget *box;
GtkWidget *image;
@@ -1423,7 +1901,7 @@ load_theme (GdmGreeterLoginWindow *login_window)
login_window);
g_signal_connect (login_window->priv->user_chooser,
"activated",
- G_CALLBACK (on_user_chosen),
+ G_CALLBACK (on_user_chooser_activated),
login_window);
g_signal_connect (login_window->priv->user_chooser,
"deactivated",
@@ -1442,30 +1920,31 @@ load_theme (GdmGreeterLoginWindow *login_window)
G_CALLBACK (on_session_activated),
login_window);
+ login_window->priv->extension_list = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "extension-list"));
+
+ g_signal_connect_swapped (GDM_EXTENSION_LIST (login_window->priv->extension_list),
+ "activated",
+ G_CALLBACK (on_extension_activated),
+ login_window);
+ g_signal_connect_swapped (GDM_EXTENSION_LIST (login_window->priv->extension_list),
+ "deactivated",
+ G_CALLBACK (on_extension_deactivated),
+ login_window);
+
login_window->priv->auth_banner_label = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-banner-label"));
/*make_label_small_italic (login_window->priv->auth_banner_label);*/
+ login_window->priv->auth_page_box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-page-box"));
button = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "cancel-button"));
g_signal_connect (button, "clicked", G_CALLBACK (cancel_button_clicked), login_window);
- entry = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "auth-prompt-entry"));
- /* Only change the invisible character if it '*' otherwise assume it is OK */
- if ('*' == gtk_entry_get_invisible_char (GTK_ENTRY (entry))) {
- gunichar invisible_char;
- invisible_char = INVISIBLE_CHAR_BLACK_CIRCLE;
- gtk_entry_set_invisible_char (GTK_ENTRY (entry), invisible_char);
- }
-
create_computer_info (login_window);
box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "computer-info-event-box"));
g_signal_connect (box, "button-press-event", G_CALLBACK (on_computer_info_label_button_press), login_window);
- if (login_window->priv->user_list_disabled) {
- switch_mode (login_window, MODE_AUTHENTICATION);
- } else {
- switch_mode (login_window, MODE_SELECTION);
- }
+ clear_active_extension (login_window);
+ switch_mode (login_window, MODE_SELECTION);
gdm_profile_end (NULL);
}
@@ -1656,6 +2135,15 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
gtk_container_class_handle_border_width (container_class);
+ 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),
@@ -1672,9 +2160,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, begin_verification),
NULL,
NULL,
- g_cclosure_marshal_VOID__VOID,
+ g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
- 0);
+ 1, G_TYPE_STRING);
signals [BEGIN_VERIFICATION_FOR_USER] =
g_signal_new ("begin-verification-for-user",
G_TYPE_FROM_CLASS (object_class),
@@ -1682,9 +2170,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, begin_verification_for_user),
NULL,
NULL,
- g_cclosure_marshal_VOID__STRING,
+ gdm_marshal_VOID__STRING_STRING,
G_TYPE_NONE,
- 1, G_TYPE_STRING);
+ 2, G_TYPE_STRING, G_TYPE_STRING);
signals [QUERY_ANSWER] =
g_signal_new ("query-answer",
G_TYPE_FROM_CLASS (object_class),
@@ -1692,9 +2180,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, query_answer),
NULL,
NULL,
- g_cclosure_marshal_VOID__STRING,
+ gdm_marshal_VOID__STRING_STRING,
G_TYPE_NONE,
- 1, G_TYPE_STRING);
+ 2, G_TYPE_STRING, G_TYPE_STRING);
signals [USER_SELECTED] =
g_signal_new ("user-selected",
G_TYPE_FROM_CLASS (object_class),
@@ -1732,9 +2220,9 @@ gdm_greeter_login_window_class_init (GdmGreeterLoginWindowClass *klass)
G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, start_session),
NULL,
NULL,
- g_cclosure_marshal_VOID__VOID,
+ g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
- 0);
+ 1, G_TYPE_STRING);
g_object_class_install_property (object_class,
PROP_DISPLAY_IS_LOCAL,
@@ -1779,6 +2267,254 @@ on_gconf_key_changed (GConfClient *client,
}
}
+static void
+on_login_extension_answer (GdmGreeterLoginWindow *login_window,
+ const char *text,
+ GdmLoginExtension *extension)
+{
+ if (text != NULL) {
+ char *service_name;
+
+ service_name = gdm_login_extension_get_service_name (extension);
+ 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_login_extension_cancel (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *extension)
+{
+ restart_conversations (login_window);
+}
+
+static gboolean
+on_login_extension_chose_user (GdmGreeterLoginWindow *login_window,
+ const char *username,
+ GdmLoginExtension *extension)
+{
+ if (!login_window->priv->user_chooser_loaded) {
+ char *name;
+
+ name = gdm_login_extension_get_name (extension);
+ 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 || login_window->priv->dialog_mode == MODE_MULTIPLE_AUTHENTICATION) {
+ return FALSE;
+ }
+
+ gdm_user_chooser_widget_set_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser),
+ username);
+
+ return TRUE;
+}
+
+static void
+on_login_extension_message_queue_empty (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *extension)
+{
+ gboolean needs_to_be_stopped;
+
+ needs_to_be_stopped = g_list_find (login_window->priv->extensions_to_stop, extension) != NULL;
+
+ if (needs_to_be_stopped) {
+ char *service_name;
+
+ service_name = gdm_login_extension_get_service_name (extension);
+ handle_stopped_conversation (login_window, service_name);
+ g_free (service_name);
+ }
+
+ if (login_window->priv->service_name_of_session_ready_to_start != NULL) {
+ if (login_window->priv->active_extension == extension) {
+ gdm_greeter_login_window_start_session (login_window);
+ }
+ } else if (login_window->priv->next_mode != MODE_UNDEFINED) {
+ reset_dialog_after_messages (login_window, login_window->priv->next_mode);
+ }
+}
+
+static void
+on_button_action_label_changed (GtkWidget *button)
+{
+ GtkAction *action;
+ char *text;
+
+ action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (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_activatable_get_related_action (GTK_ACTIVATABLE (button));
+
+ if (gtk_action_get_is_important (action)) {
+ image = gtk_action_create_icon (GTK_ACTION (action), GTK_ICON_SIZE_BUTTON);
+ } else {
+ image = NULL;
+ }
+
+ gtk_button_set_image (GTK_BUTTON (button), image);
+
+}
+
+static void
+on_button_action_tooltip_changed (GtkWidget *button)
+{
+ GtkAction *action;
+ char *text;
+
+ action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (button));
+
+ g_object_get (G_OBJECT (action), "tooltip", &text, NULL);
+
+ gtk_widget_set_tooltip_text (button, text);
+ g_free (text);
+}
+
+static GtkWidget *
+create_button_from_action (GtkAction *action)
+{
+ GtkWidget *button;
+
+ button = gtk_button_new ();
+
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (button), action);
+
+ 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_LOGIN_EXTENSION_DEFAULT_ACTION) == 0) {
+ gtk_widget_set_can_default (button, TRUE);
+ }
+
+ return button;
+}
+
+static void
+create_buttons_for_actions (GdmGreeterLoginWindow *login_window,
+ GtkActionGroup *actions)
+{
+ GList *action_list;
+ GList *node;
+ GtkWidget *box;
+
+ action_list = gtk_action_group_list_actions (actions);
+
+ box = GTK_WIDGET (gtk_builder_get_object (login_window->priv->builder, "buttonbox"));
+ for (node = action_list; node != NULL; node = node->next) {
+ GtkAction *action;
+ GtkWidget *button;
+
+ action = node->data;
+
+ button = create_button_from_action (action);
+ gtk_container_add (GTK_CONTAINER (box), button);
+ }
+
+ g_list_free (action_list);
+}
+
+static void
+gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
+ GdmLoginExtension *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_LOGIN_EXTENSION (extension));
+
+ name = gdm_login_extension_get_name (extension);
+ description = gdm_login_extension_get_description (extension);
+
+ if (!gdm_login_extension_is_visible (extension)) {
+ g_debug ("GdmGreeterLoginWindow: new extension '%s - %s' won't be added",
+ name, description);
+ g_free (name);
+ g_free (description);
+ return;
+ }
+
+ actions = gdm_login_extension_get_actions (extension);
+
+ create_buttons_for_actions (login_window, actions);
+ hide_extension_actions (extension);
+
+ g_object_unref (actions);
+
+ g_signal_connect_swapped (extension,
+ "answer",
+ G_CALLBACK (on_login_extension_answer),
+ login_window);
+ g_signal_connect_swapped (extension,
+ "cancel",
+ G_CALLBACK (on_login_extension_cancel),
+ login_window);
+ g_signal_connect_swapped (extension,
+ "user-chosen",
+ G_CALLBACK (on_login_extension_chose_user),
+ login_window);
+ g_signal_connect_swapped (extension,
+ "message-queue-empty",
+ G_CALLBACK (on_login_extension_message_queue_empty),
+ login_window);
+
+ g_debug ("GdmGreeterLoginWindow: new extension '%s - %s' added",
+ name, description);
+
+ login_window->priv->extensions = g_list_append (login_window->priv->extensions, extension);
+ service_name = gdm_login_extension_get_service_name (extension);
+
+ if (gdm_login_extension_is_choosable (extension)) {
+ gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser),
+ service_name, NULL, name, description, ~0,
+ FALSE, TRUE, NULL, NULL);
+ }
+
+ 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,
@@ -1793,6 +2529,45 @@ on_window_state_event (GtkWidget *widget,
}
static void
+load_login_extensions (GdmGreeterLoginWindow *login_window)
+{
+ GList *extensions, *node;
+ GIOExtensionPoint *extension_point;
+
+ g_debug ("GdmGreeterLoginWindow: loading extensions");
+
+ extension_point = g_io_extension_point_register (GDM_LOGIN_EXTENSION_POINT_NAME);
+ g_io_extension_point_set_required_type (extension_point,
+ GDM_TYPE_LOGIN_EXTENSION);
+
+ g_io_modules_load_all_in_directory (GDM_SIMPLE_GREETER_PLUGINS_DIR);
+
+ extensions = g_io_extension_point_get_extensions (extension_point);
+
+ if (extensions == NULL) {
+ gdm_unified_extension_load ();
+ extensions = g_io_extension_point_get_extensions (extension_point);
+ }
+
+ for (node = extensions; node != NULL; node = node->next) {
+ GIOExtension *extension;
+ GdmLoginExtension *login_extension;
+
+ extension = (GIOExtension *) node->data;
+
+ g_debug ("GdmGreeterLoginWindow: adding extension '%s'",
+ g_io_extension_get_name (extension));
+
+ login_extension = g_object_new (g_io_extension_get_type (extension), NULL);
+
+ gdm_greeter_login_window_add_extension (GDM_GREETER_LOGIN_WINDOW (login_window),
+ login_extension);
+ }
+
+ g_debug ("GdmGreeterLoginWindow: done loading extensions");
+}
+
+static void
gdm_greeter_login_window_init (GdmGreeterLoginWindow *login_window)
{
GConfClient *client;
@@ -1804,8 +2579,7 @@ gdm_greeter_login_window_init (GdmGreeterLoginWindow *login_window)
login_window->priv = GDM_GREETER_LOGIN_WINDOW_GET_PRIVATE (login_window);
login_window->priv->timed_login_enabled = FALSE;
login_window->priv->dialog_mode = MODE_UNDEFINED;
- login_window->priv->message_queue = g_queue_new ();
- login_window->priv->message_queue_empty_reset_dialog_mode = MODE_UNDEFINED;
+ login_window->priv->next_mode = MODE_UNDEFINED;
client = gconf_client_get_default ();
error = NULL;
@@ -1850,6 +2624,7 @@ gdm_greeter_login_window_init (GdmGreeterLoginWindow *login_window)
login_window,
NULL,
NULL);
+ g_idle_add ((GSourceFunc) load_login_extensions, login_window);
gdm_profile_end (NULL);
}
@@ -1869,9 +2644,6 @@ gdm_greeter_login_window_finalize (GObject *object)
g_object_unref (login_window->priv->client);
}
- purge_message_queue (login_window);
- g_queue_free (login_window->priv->message_queue);
-
G_OBJECT_CLASS (gdm_greeter_login_window_parent_class)->finalize (object);
}
diff --git a/gui/simple-greeter/gdm-greeter-login-window.h b/gui/simple-greeter/gdm-greeter-login-window.h
index f461c8a5..bd08d663 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.h
+++ b/gui/simple-greeter/gdm-greeter-login-window.h
@@ -23,6 +23,7 @@
#define __GDM_GREETER_LOGIN_WINDOW_H
#include <glib-object.h>
+#include "gdm-login-extension.h"
G_BEGIN_DECLS
@@ -46,12 +47,17 @@ typedef struct
GtkWindowClass parent_class;
/* signals */
+ void (* start_conversation) (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
void (* begin_auto_login) (GdmGreeterLoginWindow *login_window,
const char *username);
- void (* begin_verification) (GdmGreeterLoginWindow *login_window);
+ void (* begin_verification) (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
void (* begin_verification_for_user) (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
const char *username);
void (* query_answer) (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
const char *text);
void (* user_selected) (GdmGreeterLoginWindow *login_window,
const char *text);
@@ -67,23 +73,33 @@ GtkWidget * gdm_greeter_login_window_new (gboolean displa
gboolean gdm_greeter_login_window_reset (GdmGreeterLoginWindow *login_window);
-gboolean gdm_greeter_login_window_authentication_failed (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_set_default_session_name (GdmGreeterLoginWindow *login_window,
const char *text);
+gboolean gdm_greeter_login_window_service_unavailable (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
+
void gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_window,
const char *username,
int delay);
-void gdm_greeter_login_window_session_opened (GdmGreeterLoginWindow *login_window);
+void gdm_greeter_login_window_session_opened (GdmGreeterLoginWindow *login_window,
+ const char *service_name);
G_END_DECLS
diff --git a/gui/simple-greeter/gdm-greeter-login-window.ui b/gui/simple-greeter/gdm-greeter-login-window.ui
index 4f6bed43..2ab29cbf 100644
--- a/gui/simple-greeter/gdm-greeter-login-window.ui
+++ b/gui/simple-greeter/gdm-greeter-login-window.ui
@@ -158,69 +158,40 @@
<child>
<object class="GtkVBox" id="selection-box">
<property name="visible">True</property>
- <property name="spacing">10</property>
+ <property name="spacing">2</property>
<child>
- <object class="GdmUserChooserWidget" id="user-chooser">
- <property name="visible">False</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="auth-input-box">
+ <object class="GtkAlignment" id="task-list-alignment">
<property name="visible">True</property>
- <property name="spacing">6</property>
+ <property name="xalign">1.0</property>
+ <property name="xscale">0.0</property>
<child>
- <object class="GtkLabel" id="auth-prompt-label">
- <property name="visible">True</property>
-
- <accessibility>
- <relation type="label-for" target="auth-prompt-entry"/>
- </accessibility>
+ <object class="GdmExtensionList" id="extension-list">
+ <property name="visible">False</property>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="auth-prompt-entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="activates_default">True</property>
- <accessibility>
- <relation type="labelled-by" target="auth-prompt-label"/>
- </accessibility>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GdmUserChooserWidget" id="user-chooser">
+ <property name="visible">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkHBox" id="auth-message-box">
+ <object class="GtkHBox" id="auth-page-box">
<property name="visible">True</property>
+ <property name="border_width">10</property>
<child>
- <object class="GtkLabel" id="auth-message-label">
- <property name="visible">True</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
+ <placeholder/>
</child>
</object>
<packing>
diff --git a/gui/simple-greeter/gdm-greeter-session.c b/gui/simple-greeter/gdm-greeter-session.c
index d939f472..60acc45b 100644
--- a/gui/simple-greeter/gdm-greeter-session.c
+++ b/gui/simple-greeter/gdm-greeter-session.c
@@ -75,7 +75,7 @@ on_info (GdmGreeterClient *client,
{
g_debug ("GdmGreeterSession: Info: %s", text);
- gdm_greeter_login_window_info (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+ gdm_greeter_login_window_info (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
}
static void
@@ -86,7 +86,17 @@ on_problem (GdmGreeterClient *client,
{
g_debug ("GdmGreeterSession: Problem: %s", text);
- gdm_greeter_login_window_problem (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+ gdm_greeter_login_window_problem (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
+}
+
+static void
+on_service_unavailable (GdmGreeterClient *client,
+ const char *service_name,
+ GdmGreeterSession *session)
+{
+ g_debug ("GdmGreeterSession: Service Unavailable: %s", service_name);
+
+ gdm_greeter_login_window_service_unavailable (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name);
}
static void
@@ -96,40 +106,30 @@ on_ready (GdmGreeterClient *client,
{
g_debug ("GdmGreeterSession: Ready");
- gdm_greeter_login_window_ready (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+ gdm_greeter_login_window_ready (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window),
+ service_name);
}
static void
-on_reset (GdmGreeterClient *client,
- GdmGreeterSession *session)
+on_conversation_stopped (GdmGreeterClient *client,
+ const char *service_name,
+ GdmGreeterSession *session)
{
- g_debug ("GdmGreeterSession: Reset");
-
- session->priv->num_tries = 0;
+ g_debug ("GdmGreeterSession: Conversation '%s' stopped", service_name);
- gdm_greeter_login_window_reset (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+ gdm_greeter_login_window_conversation_stopped (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window),
+ service_name);
}
static void
-on_authentication_failed (GdmGreeterClient *client,
- GdmGreeterSession *session)
+on_reset (GdmGreeterClient *client,
+ GdmGreeterSession *session)
{
- g_debug ("GdmGreeterSession: Authentication failed");
-
- session->priv->num_tries++;
-
- if (session->priv->num_tries < MAX_LOGIN_TRIES) {
- g_debug ("GdmGreeterSession: Retrying login (%d)",
- session->priv->num_tries);
+ g_debug ("GdmGreeterSession: Reset");
- gdm_greeter_login_window_authentication_failed (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
- } else {
- g_debug ("GdmGreeterSession: Maximum number of login tries exceeded (%d) - resetting",
- session->priv->num_tries - 1);
- session->priv->num_tries = 0;
+ session->priv->num_tries = 0;
- gdm_greeter_login_window_reset (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
- }
+ gdm_greeter_login_window_reset (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
}
static void
@@ -181,10 +181,11 @@ on_timed_login_requested (GdmGreeterClient *client,
static void
on_session_opened (GdmGreeterClient *client,
+ const char *service_name,
GdmGreeterSession *session)
{
g_debug ("GdmGreeterSession: session opened");
- gdm_greeter_login_window_session_opened (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+ gdm_greeter_login_window_session_opened (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name);
}
static void
@@ -195,7 +196,7 @@ on_info_query (GdmGreeterClient *client,
{
g_debug ("GdmGreeterSession: Info query: %s", text);
- gdm_greeter_login_window_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+ gdm_greeter_login_window_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
}
static void
@@ -206,10 +207,18 @@ on_secret_info_query (GdmGreeterClient *client,
{
g_debug ("GdmGreeterSession: Secret info query: %s", text);
- gdm_greeter_login_window_secret_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+ gdm_greeter_login_window_secret_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
}
static void
+on_start_conversation (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
+ GdmGreeterSession *session)
+{
+ gdm_greeter_client_call_start_conversation (session->priv->client,
+ service_name);
+}
+static void
on_begin_auto_login (GdmGreeterLoginWindow *login_window,
const char *username,
GdmGreeterSession *session)
@@ -220,29 +229,32 @@ on_begin_auto_login (GdmGreeterLoginWindow *login_window,
static void
on_begin_verification (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
GdmGreeterSession *session)
{
gdm_greeter_client_call_begin_verification (session->priv->client,
- "gdm");
+ service_name);
}
static void
on_begin_verification_for_user (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
const char *username,
GdmGreeterSession *session)
{
gdm_greeter_client_call_begin_verification_for_user (session->priv->client,
- "gdm",
+ service_name,
username);
}
static void
on_query_answer (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
const char *text,
GdmGreeterSession *session)
{
gdm_greeter_client_call_answer_query (session->priv->client,
- "gdm",
+ service_name,
text);
}
@@ -270,7 +282,6 @@ on_cancelled (GdmGreeterLoginWindow *login_window,
GdmGreeterSession *session)
{
gdm_greeter_client_call_cancel (session->priv->client);
- gdm_greeter_client_call_start_conversation (session->priv->client, "gdm");
}
static void
@@ -281,9 +292,10 @@ on_disconnected (GdmGreeterSession *session)
static void
on_start_session (GdmGreeterLoginWindow *login_window,
+ const char *service_name,
GdmGreeterSession *session)
{
- gdm_greeter_client_call_start_session_when_ready (session->priv->client, "gdm", TRUE);
+ gdm_greeter_client_call_start_session_when_ready (session->priv->client, service_name, TRUE);
}
static int
@@ -368,7 +380,10 @@ toggle_login_window (GdmGreeterSession *session,
is_local = gdm_greeter_client_get_display_is_local (session->priv->client);
g_debug ("GdmGreeterSession: Starting a login window local:%d", is_local);
session->priv->login_window = gdm_greeter_login_window_new (is_local);
-
+ g_signal_connect (session->priv->login_window,
+ "start-conversation",
+ G_CALLBACK (on_start_conversation),
+ session);
g_signal_connect (session->priv->login_window,
"begin-auto-login",
G_CALLBACK (on_begin_auto_login),
@@ -424,8 +439,6 @@ gdm_greeter_session_start (GdmGreeterSession *session,
toggle_panel (session, TRUE);
toggle_login_window (session, TRUE);
- gdm_greeter_client_call_start_conversation (session->priv->client, "gdm");
-
gdm_profile_end (NULL);
return res;
@@ -559,16 +572,20 @@ gdm_greeter_session_init (GdmGreeterSession *session)
G_CALLBACK (on_problem),
session);
g_signal_connect (session->priv->client,
+ "service-unavailable",
+ G_CALLBACK (on_service_unavailable),
+ session);
+ g_signal_connect (session->priv->client,
"ready",
G_CALLBACK (on_ready),
session);
g_signal_connect (session->priv->client,
- "reset",
- G_CALLBACK (on_reset),
+ "conversation-stopped",
+ G_CALLBACK (on_conversation_stopped),
session);
g_signal_connect (session->priv->client,
- "authentication-failed",
- G_CALLBACK (on_authentication_failed),
+ "reset",
+ G_CALLBACK (on_reset),
session);
g_signal_connect (session->priv->client,
"selected-user-changed",
diff --git a/gui/simple-greeter/gdm-user-chooser-widget.c b/gui/simple-greeter/gdm-user-chooser-widget.c
index 0f73cc5a..60ed1602 100644
--- a/gui/simple-greeter/gdm-user-chooser-widget.c
+++ b/gui/simple-greeter/gdm-user-chooser-widget.c
@@ -654,9 +654,30 @@ gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
char *
gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget)
{
+ char *active_item_id;
+ gboolean isnt_user;
+
g_return_val_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget), NULL);
- return gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
+ active_item_id = gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
+ if (active_item_id == NULL) {
+ g_debug ("GdmUserChooserWidget: no active item in list");
+ return NULL;
+ }
+
+ gdm_chooser_widget_lookup_item (GDM_CHOOSER_WIDGET (widget), active_item_id,
+ NULL, NULL, NULL, NULL, NULL,
+ &isnt_user);
+
+ if (isnt_user) {
+ g_debug ("GdmUserChooserWidget: active item '%s' isn't a user", active_item_id);
+ g_free (active_item_id);
+ return NULL;
+ }
+
+ g_debug ("GdmUserChooserWidget: active item '%s' is a user", active_item_id);
+
+ return active_item_id;
}
void
diff --git a/gui/simple-greeter/libgdmsimplegreeter/Makefile.am b/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
new file mode 100644
index 00000000..07b55afa
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
@@ -0,0 +1,43 @@
+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-login-extension.h \
+ gdm-login-extension.c \
+ $(NULL)
+
+libgdmsimplegreeter_la_LIBADD = \
+ $(GTK_LIBS) \
+ $(top_builddir)/common/libgdmcommon.la \
+ $(NULL)
+
+libgdmsimplegreeter_la_LDFLAGS = \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+ -no-undefined \
+ $(NULL)
+
+headersdir = $(includedir)/gdm/simple-greeter
+headers_HEADERS = gdm-login-extension.h
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = gdmsimplegreeter.pc
+
+EXTRA_DIST = gdmsimplegreeter.pc
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.c b/gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.c
new file mode 100644
index 00000000..ae620e38
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-login-extension.h"
+#include "gdm-marshal.h"
+
+enum {
+ ENABLED,
+ DISABLED,
+ ANSWER,
+ USER_CHOSEN,
+ CANCEL,
+ MESSAGE_QUEUE_EMPTY,
+ NUMBER_OF_SIGNALS
+};
+
+static guint signals [NUMBER_OF_SIGNALS] = { 0, };
+
+static void gdm_login_extension_class_init (gpointer g_iface);
+
+GType
+gdm_login_extension_get_type (void)
+{
+ static GType login_extension_type = 0;
+
+ if (!login_extension_type) {
+ login_extension_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+ "GdmLoginExtension",
+ sizeof (GdmLoginExtensionIface),
+ (GClassInitFunc) gdm_login_extension_class_init,
+ 0, NULL, 0);
+
+ g_type_interface_add_prerequisite (login_extension_type, G_TYPE_OBJECT);
+ }
+
+ return login_extension_type;
+}
+
+static void
+gdm_login_extension_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 (GdmLoginExtensionIface, 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 (GdmLoginExtensionIface, disabled),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [ANSWER] =
+ g_signal_new ("answer",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmLoginExtensionIface, 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 (GdmLoginExtensionIface, 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 (GdmLoginExtensionIface, cancel),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals [MESSAGE_QUEUE_EMPTY] =
+ g_signal_new ("message-queue-empty",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmLoginExtensionIface, message_queue_empty),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+GIcon *
+gdm_login_extension_get_icon (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->get_icon (extension);
+}
+
+char *
+gdm_login_extension_get_description (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->get_description (extension);
+}
+
+char *
+gdm_login_extension_get_name (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->get_name (extension);
+}
+
+void
+gdm_login_extension_set_enabled (GdmLoginExtension *extension,
+ gboolean should_enable)
+{
+ g_object_set_data (G_OBJECT (extension),
+ "gdm-greeter-extension-is-disabled",
+ GINT_TO_POINTER (!should_enable));
+
+ if (should_enable) {
+ g_signal_emit (G_OBJECT (extension), signals [ENABLED], 0);
+ } else {
+ g_signal_emit (G_OBJECT (extension), signals [DISABLED], 0);
+ }
+}
+
+gboolean
+gdm_login_extension_is_enabled (GdmLoginExtension *extension)
+{
+ return !g_object_get_data (G_OBJECT (extension), "gdm-greeter-extension-is-disabled");
+}
+
+gboolean
+gdm_login_extension_is_choosable (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->is_choosable (extension);
+}
+
+gboolean
+gdm_login_extension_is_visible (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->is_visible (extension);
+}
+
+void
+gdm_login_extension_queue_message (GdmLoginExtension *extension,
+ GdmServiceMessageType type,
+ const char *message)
+{
+ GDM_LOGIN_EXTENSION_GET_IFACE (extension)->queue_message (extension,
+ type,
+ message);
+}
+
+void
+gdm_login_extension_ask_question (GdmLoginExtension *extension,
+ const char *message)
+{
+ GDM_LOGIN_EXTENSION_GET_IFACE (extension)->ask_question (extension,
+ message);
+}
+
+void
+gdm_login_extension_ask_secret (GdmLoginExtension *extension,
+ const char *message)
+{
+ GDM_LOGIN_EXTENSION_GET_IFACE (extension)->ask_secret (extension,
+ message);
+
+}
+
+void
+gdm_login_extension_reset (GdmLoginExtension *extension)
+{
+ GDM_LOGIN_EXTENSION_GET_IFACE (extension)->reset (extension);
+}
+
+void
+gdm_login_extension_set_ready (GdmLoginExtension *extension)
+{
+ GDM_LOGIN_EXTENSION_GET_IFACE (extension)->set_ready (extension);
+}
+
+gboolean
+gdm_login_extension_focus (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->focus (extension);
+}
+
+char *
+gdm_login_extension_get_service_name (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->get_service_name (extension);
+
+}
+
+gboolean
+gdm_login_extension_has_queued_messages (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->has_queued_messages (extension);
+}
+
+GtkWidget *
+gdm_login_extension_get_page (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->get_page (extension);
+}
+
+GtkActionGroup *
+gdm_login_extension_get_actions (GdmLoginExtension *extension)
+{
+ return GDM_LOGIN_EXTENSION_GET_IFACE (extension)->get_actions (extension);
+}
+
+void
+_gdm_login_extension_emit_answer (GdmLoginExtension *extension,
+ const char *answer)
+{
+ g_signal_emit (extension, signals [ANSWER], 0, answer);
+
+}
+
+void
+_gdm_login_extension_emit_cancel (GdmLoginExtension *extension)
+{
+ g_signal_emit (extension, signals [CANCEL], 0);
+}
+
+gboolean
+_gdm_login_extension_emit_choose_user (GdmLoginExtension *extension,
+ const char *username)
+{
+ gboolean was_chosen;
+
+ was_chosen = FALSE;
+
+ g_signal_emit (extension, signals [USER_CHOSEN], 0, username, &was_chosen);
+
+ return was_chosen;
+}
+
+void
+_gdm_login_extension_emit_message_queue_empty (GdmLoginExtension *extension)
+{
+ g_signal_emit (extension, signals [MESSAGE_QUEUE_EMPTY], 0);
+
+}
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.h b/gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.h
new file mode 100644
index 00000000..7f56de19
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdm-login-extension.h
@@ -0,0 +1,128 @@
+/*
+ * 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_LOGIN_EXTENSION_H
+#define __GDM_LOGIN_EXTENSION_H
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_LOGIN_EXTENSION (gdm_login_extension_get_type ())
+#define GDM_LOGIN_EXTENSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_LOGIN_EXTENSION, GdmLoginExtension))
+#define GDM_LOGIN_EXTENSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_LOGIN_EXTENSION, GdmLoginExtensionClass))
+#define GDM_IS_LOGIN_EXTENSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_LOGIN_EXTENSION))
+#define GDM_LOGIN_EXTENSION_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_LOGIN_EXTENSION, GdmLoginExtensionIface))
+
+#define GDM_LOGIN_EXTENSION_POINT_NAME "gdm-login"
+#define GDM_LOGIN_EXTENSION_DEFAULT_ACTION "default-action"
+#define GDM_LOGIN_EXTENSION_OTHER_USER "__other"
+
+typedef struct _GdmLoginExtension GdmLoginExtension;
+typedef struct _GdmLoginExtensionIface GdmLoginExtensionIface;
+
+typedef enum {
+ GDM_SERVICE_MESSAGE_TYPE_INFO,
+ GDM_SERVICE_MESSAGE_TYPE_PROBLEM
+} GdmServiceMessageType;
+
+struct _GdmLoginExtensionIface
+{
+ GTypeInterface base_iface;
+
+ /* methods */
+ GIcon * (* get_icon) (GdmLoginExtension *extension);
+ char * (* get_description) (GdmLoginExtension *extension);
+ char * (* get_name) (GdmLoginExtension *extension);
+
+ gboolean (* is_choosable) (GdmLoginExtension *extension);
+ gboolean (* is_visible) (GdmLoginExtension *extension);
+
+ void (* queue_message) (GdmLoginExtension *extension,
+ GdmServiceMessageType type,
+ const char *message);
+ void (* ask_question) (GdmLoginExtension *extension,
+ const char *message);
+ void (* ask_secret) (GdmLoginExtension *extension,
+ const char *message);
+ void (* reset) (GdmLoginExtension *extension);
+ void (* set_ready) (GdmLoginExtension *extension);
+ char * (* get_service_name) (GdmLoginExtension *extension);
+ GtkWidget * (* get_page) (GdmLoginExtension *extension);
+ GtkActionGroup * (* get_actions) (GdmLoginExtension *extension);
+ void (* request_answer) (GdmLoginExtension *extension);
+ gboolean (* has_queued_messages) (GdmLoginExtension *extension);
+ gboolean (* focus) (GdmLoginExtension *extension);
+
+ /* signals */
+ void (* enabled) (GdmLoginExtension *extension);
+ void (* disabled) (GdmLoginExtension *extension);
+ char * (* answer) (GdmLoginExtension *extension);
+ void (* cancel) (GdmLoginExtension *extension);
+ gboolean (* user_chosen) (GdmLoginExtension *extension);
+ gboolean (* message_queue_empty) (GdmLoginExtension *extension);
+};
+
+GType gdm_login_extension_get_type (void) G_GNUC_CONST;
+
+GIcon *gdm_login_extension_get_icon (GdmLoginExtension *extension);
+char *gdm_login_extension_get_description (GdmLoginExtension *extension);
+char *gdm_login_extension_get_name (GdmLoginExtension *extension);
+void gdm_login_extension_set_enabled (GdmLoginExtension *extension,
+ gboolean should_enable);
+gboolean gdm_login_extension_is_enabled (GdmLoginExtension *extension);
+gboolean gdm_login_extension_is_choosable (GdmLoginExtension *extension);
+gboolean gdm_login_extension_is_visible (GdmLoginExtension *extension);
+
+void gdm_login_extension_queue_message (GdmLoginExtension *extension,
+ GdmServiceMessageType type,
+ const char *message);
+
+void gdm_login_extension_ask_question (GdmLoginExtension *extension,
+ const char *message);
+
+void gdm_login_extension_ask_secret (GdmLoginExtension *extension,
+ const char *message);
+
+void gdm_login_extension_reset (GdmLoginExtension *extension);
+void gdm_login_extension_set_ready (GdmLoginExtension *extension);
+gboolean gdm_login_extension_focus (GdmLoginExtension *extension);
+
+char *gdm_login_extension_get_service_name (GdmLoginExtension *extension);
+gboolean gdm_login_extension_has_queued_messages (GdmLoginExtension *extension);
+
+GtkWidget *gdm_login_extension_get_page (GdmLoginExtension *extension);
+GtkActionGroup *gdm_login_extension_get_actions (GdmLoginExtension *extension);
+
+
+/* protected
+ */
+void _gdm_login_extension_emit_answer (GdmLoginExtension *extension,
+ const char *answer);
+void _gdm_login_extension_emit_cancel (GdmLoginExtension *extension);
+gboolean _gdm_login_extension_emit_choose_user (GdmLoginExtension *extension,
+ const char *username);
+
+void _gdm_login_extension_emit_message_queue_empty (GdmLoginExtension *extension);
+
+
+G_END_DECLS
+#endif /* __GDM_LOGIN_EXTENSION_H */
diff --git a/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in b/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
new file mode 100644
index 00000000..cf8c9aca
--- /dev/null
+++ b/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+extensionsdir=@GDM_SIMPLE_GREETER_PLUGINS_DIR@
+
+Name: GDM Simple Greeter
+Description: Library for GDM Simple Greeter Plugins
+Version: @VERSION@
+Libs: -L${libdir} -lgdmsimplegreeter
+Cflags: -I${includedir}/gdm/simple-greeter
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d2f59a71..cb458546 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -81,6 +81,7 @@ gui/simple-greeter/gdm-simple-greeter.schemas.in
gui/simple-greeter/gdm-timer.c
gui/simple-greeter/gdm-user-chooser-widget.c
gui/simple-greeter/greeter-main.c
+gui/simple-greeter/extensions/password/gdm-password-extension.c
utils/gdmflexiserver.c
utils/gdm-screenshot.c