diff options
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 |