summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2009-01-30 23:57:31 -0500
committerRay Strode <rstrode@redhat.com>2011-06-13 20:33:47 -0400
commit768fe8d5e9e96c25a006f084ad6452d076d03b60 (patch)
tree1feae709bd54cbabb854221209ef3c0097589837
parent0d92891917576227b40eca56c4b173b879aa8792 (diff)
downloadgdm-768fe8d5e9e96c25a006f084ad6452d076d03b60.tar.gz
greeter: Add a login extension mechanism to greeter
This allows extensions to drive which PAM conversations get run and potentially augment the login window UI. This commit adds one builtin extension that does the traditional unified authentication thing, and a plugin for password-only based authentication. By default we use the builtin extension, but enable the plugin with --enable-split-authentication Subsequent commits will add support for fingerprint and smartcard plugins.
-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