summaryrefslogtreecommitdiff
path: root/src/mate-screensaver-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mate-screensaver-dialog.c')
-rw-r--r--src/mate-screensaver-dialog.c665
1 files changed, 665 insertions, 0 deletions
diff --git a/src/mate-screensaver-dialog.c b/src/mate-screensaver-dialog.c
new file mode 100644
index 0000000..d698159
--- /dev/null
+++ b/src/mate-screensaver-dialog.c
@@ -0,0 +1,665 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-lock-plug.h"
+
+#include "gs-auth.h"
+#include "setuid.h"
+
+#include "gs-debug.h"
+
+#define MAX_FAILURES 5
+
+static gboolean verbose = FALSE;
+static gboolean show_version = FALSE;
+static gboolean enable_logout = FALSE;
+static gboolean enable_switch = FALSE;
+static char *logout_command = NULL;
+static char *status_message = NULL;
+static char *away_message = NULL;
+
+static GOptionEntry entries [] =
+{
+ {
+ "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose,
+ N_("Show debugging output"), NULL
+ },
+ {
+ "version", 0, 0, G_OPTION_ARG_NONE, &show_version,
+ N_("Version of this application"), NULL
+ },
+ {
+ "enable-logout", 0, 0, G_OPTION_ARG_NONE, &enable_logout,
+ N_("Show the logout button"), NULL
+ },
+ {
+ "logout-command", 0, 0, G_OPTION_ARG_STRING, &logout_command,
+ N_("Command to invoke from the logout button"), NULL
+ },
+ {
+ "enable-switch", 0, 0, G_OPTION_ARG_NONE, &enable_switch,
+ N_("Show the switch user button"), NULL
+ },
+ {
+ "status-message", 0, 0, G_OPTION_ARG_STRING, &status_message,
+ N_("Message to show in the dialog"), N_("MESSAGE")
+ },
+ {
+ "away-message", 0, 0, G_OPTION_ARG_STRING, &away_message,
+ N_("Not used"), N_("MESSAGE")
+ },
+ { NULL }
+};
+
+static char *
+get_id_string (GtkWidget *widget)
+{
+ char *id = NULL;
+
+ g_return_val_if_fail (widget != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ id = g_strdup_printf ("%" G_GUINT32_FORMAT,
+ (guint32) GDK_WINDOW_XID (widget->window));
+ return id;
+}
+
+static gboolean
+print_id (GtkWidget *widget)
+{
+ char *id;
+
+ gs_profile_start (NULL);
+
+ id = get_id_string (widget);
+ printf ("WINDOW ID=%s\n", id);
+ fflush (stdout);
+
+ gs_profile_end (NULL);
+
+ g_free (id);
+
+ return FALSE;
+}
+
+static void
+response_cancel (void)
+{
+ printf ("RESPONSE=CANCEL\n");
+ fflush (stdout);
+}
+
+static void
+response_ok (void)
+{
+ printf ("RESPONSE=OK\n");
+ fflush (stdout);
+}
+
+static gboolean
+quit_response_ok (void)
+{
+ response_ok ();
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+quit_response_cancel (void)
+{
+ response_cancel ();
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static void
+response_lock_init_failed (void)
+{
+ /* if we fail to lock then we should drop the dialog */
+ response_ok ();
+}
+
+static char *
+request_response (GSLockPlug *plug,
+ const char *prompt,
+ gboolean visible)
+{
+ int response;
+ char *text;
+
+ gs_lock_plug_set_sensitive (plug, TRUE);
+ gs_lock_plug_enable_prompt (plug, prompt, visible);
+ response = gs_lock_plug_run (plug);
+
+ gs_debug ("got response: %d", response);
+
+ text = NULL;
+ if (response == GS_LOCK_PLUG_RESPONSE_OK)
+ {
+ gs_lock_plug_get_text (plug, &text);
+ }
+ gs_lock_plug_disable_prompt (plug);
+
+ return text;
+}
+
+/* Adapted from MDM2 daemon/verify-pam.c on 2006-06-13 */
+static const char *
+maybe_translate_message (const char *msg)
+{
+ char *s;
+ const char *ret;
+ static GHashTable *hash = NULL;
+
+ if (hash == NULL)
+ {
+ /* Here we come with some fairly standard messages so that
+ we have as much as possible translated. Should really be
+ translated in pam I suppose. This way we can "change"
+ some of these messages to be more sane. */
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ /* login: is whacked always translate to Username: */
+ g_hash_table_insert (hash, "login:", _("Username:"));
+ g_hash_table_insert (hash, "Username:", _("Username:"));
+ g_hash_table_insert (hash, "username:", _("Username:"));
+ g_hash_table_insert (hash, "Password:", _("Password:"));
+ g_hash_table_insert (hash, "password:", _("Password:"));
+ g_hash_table_insert (hash, "You are required to change your password immediately (password aged)", _("You are required to change your password immediately (password aged)"));
+ g_hash_table_insert (hash, "You are required to change your password immediately (root enforced)", _("You are required to change your password immediately (root enforced)"));
+ g_hash_table_insert (hash, "Your account has expired; please contact your system administrator", _("Your account has expired; please contact your system administrator"));
+ g_hash_table_insert (hash, "No password supplied", _("No password supplied"));
+ g_hash_table_insert (hash, "Password unchanged", _("Password unchanged"));
+ g_hash_table_insert (hash, "Can not get username", _("Can not get username"));
+ g_hash_table_insert (hash, "Retype new UNIX password:", _("Retype new UNIX password:"));
+ g_hash_table_insert (hash, "Enter new UNIX password:", _("Enter new UNIX password:"));
+ g_hash_table_insert (hash, "(current) UNIX password:", _("(current) UNIX password:"));
+ g_hash_table_insert (hash, "Error while changing NIS password.", _("Error while changing NIS password."));
+ g_hash_table_insert (hash, "You must choose a longer password", _("You must choose a longer password"));
+ g_hash_table_insert (hash, "Password has been already used. Choose another.", _("Password has been already used. Choose another."));
+ g_hash_table_insert (hash, "You must wait longer to change your password", _("You must wait longer to change your password"));
+ g_hash_table_insert (hash, "Sorry, passwords do not match", _("Sorry, passwords do not match"));
+ /* FIXME: what about messages which have some variables in them, perhaps try to do those as well */
+ }
+
+ s = g_strstrip (g_strdup (msg));
+ ret = g_hash_table_lookup (hash, s);
+ g_free (s);
+
+ if (ret != NULL)
+ {
+ return ret;
+ }
+ else
+ {
+ return msg;
+ }
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+ GSLockPlug *plug;
+ const char *message;
+
+ plug = GS_LOCK_PLUG (data);
+
+ gs_profile_start (NULL);
+ gs_debug ("Got message style %d: '%s'", style, msg);
+
+ gtk_widget_show (GTK_WIDGET (plug));
+ gs_lock_plug_set_ready (plug);
+
+ ret = TRUE;
+ *response = NULL;
+ message = maybe_translate_message (msg);
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ if (msg != NULL)
+ {
+ char *resp;
+ resp = request_response (plug, message, TRUE);
+ *response = resp;
+ }
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ if (msg != NULL)
+ {
+ char *resp;
+ resp = request_response (plug, message, FALSE);
+ *response = resp;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ gs_lock_plug_show_message (plug, message);
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ gs_lock_plug_show_message (plug, message);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (*response == NULL)
+ {
+ gs_debug ("Got no response");
+ ret = FALSE;
+ }
+ else
+ {
+ gs_lock_plug_show_message (plug, _("Checking..."));
+ gs_lock_plug_set_sensitive (plug, FALSE);
+ }
+
+ /* we may have pending events that should be processed before continuing back into PAM */
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ gs_lock_plug_set_busy (plug);
+ gs_profile_end (NULL);
+
+ return ret;
+}
+
+static gboolean
+reset_idle_cb (GSLockPlug *plug)
+{
+ gs_lock_plug_set_sensitive (plug, TRUE);
+ gs_lock_plug_show_message (plug, NULL);
+
+ return FALSE;
+}
+
+static gboolean
+do_auth_check (GSLockPlug *plug)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+
+ gs_lock_plug_disable_prompt (plug);
+ gs_lock_plug_set_busy (plug);
+ res = gs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, plug, &error);
+
+ gs_debug ("Verify user returned: %s", res ? "TRUE" : "FALSE");
+ if (! res)
+ {
+ if (error != NULL)
+ {
+ gs_debug ("Verify user returned error: %s", error->message);
+ gs_lock_plug_show_message (plug, error->message);
+ }
+ else
+ {
+ gs_lock_plug_show_message (plug, _("Authentication failed."));
+ }
+
+ printf ("NOTICE=AUTH FAILED\n");
+ fflush (stdout);
+
+ if (error != NULL)
+ {
+ g_error_free (error);
+ }
+ }
+
+ return res;
+}
+
+static void
+response_cb (GSLockPlug *plug,
+ gint response_id)
+{
+ if ((response_id == GS_LOCK_PLUG_RESPONSE_CANCEL) ||
+ (response_id == GTK_RESPONSE_DELETE_EVENT))
+ {
+ quit_response_cancel ();
+ }
+}
+
+static gboolean
+response_request_quit (void)
+{
+ printf ("REQUEST QUIT\n");
+ fflush (stdout);
+ return FALSE;
+}
+
+static gboolean
+quit_timeout_cb (gpointer data)
+{
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+auth_check_idle (GSLockPlug *plug)
+{
+ gboolean res;
+ gboolean again;
+ static guint loop_counter = 0;
+
+ again = TRUE;
+ res = do_auth_check (plug);
+
+ if (res)
+ {
+ again = FALSE;
+ g_idle_add ((GSourceFunc)quit_response_ok, NULL);
+ }
+ else
+ {
+ loop_counter++;
+
+ if (loop_counter < MAX_FAILURES)
+ {
+ gs_debug ("Authentication failed, retrying (%u)", loop_counter);
+ g_timeout_add (3000, (GSourceFunc)reset_idle_cb, plug);
+ }
+ else
+ {
+ gs_debug ("Authentication failed, quitting (max failures)");
+ again = FALSE;
+ /* Don't quit immediately, but rather request that mate-screensaver
+ * terminates us after it has finished the dialog shake. Time out
+ * after 5 seconds and quit anyway if this doesn't happen though */
+ g_idle_add ((GSourceFunc)response_request_quit, NULL);
+ g_timeout_add (5000, (GSourceFunc)quit_timeout_cb, NULL);
+ }
+ }
+
+ return again;
+}
+
+static void
+show_cb (GtkWidget *widget,
+ gpointer data)
+{
+ print_id (widget);
+}
+
+static gboolean
+popup_dialog_idle (void)
+{
+ GtkWidget *widget;
+
+ gs_profile_start (NULL);
+
+ widget = gs_lock_plug_new ();
+
+ if (enable_logout)
+ {
+ g_object_set (widget, "logout-enabled", TRUE, NULL);
+ }
+
+ if (logout_command)
+ {
+ g_object_set (widget, "logout-command", logout_command, NULL);
+ }
+
+ if (enable_switch)
+ {
+ g_object_set (widget, "switch-enabled", TRUE, NULL);
+ }
+
+ if (status_message)
+ {
+ g_object_set (widget, "status-message", status_message, NULL);
+ }
+
+ g_signal_connect (GS_LOCK_PLUG (widget), "response", G_CALLBACK (response_cb), NULL);
+ g_signal_connect (widget, "show", G_CALLBACK (show_cb), NULL);
+
+ gtk_widget_realize (widget);
+
+ g_idle_add ((GSourceFunc)auth_check_idle, widget);
+
+ gs_profile_end (NULL);
+
+ return FALSE;
+}
+
+
+/*
+ * Copyright (c) 1991-2004 Jamie Zawinski <[email protected]>
+ * Copyright (c) 2005 William Jon McCann <[email protected]>
+ *
+ * Initializations that potentially take place as a priveleged user:
+ If the executable is setuid root, then these initializations
+ are run as root, before discarding privileges.
+*/
+static gboolean
+privileged_initialization (int *argc,
+ char **argv,
+ gboolean verbose)
+{
+ gboolean ret;
+ char *nolock_reason;
+ char *orig_uid;
+ char *uid_message;
+
+ gs_profile_start (NULL);
+
+#ifndef NO_LOCKING
+ /* before hack_uid () for proper permissions */
+ gs_auth_priv_init ();
+#endif /* NO_LOCKING */
+
+ ret = hack_uid (&nolock_reason,
+ &orig_uid,
+ &uid_message);
+
+ if (nolock_reason)
+ {
+ g_debug ("Locking disabled: %s", nolock_reason);
+ }
+
+ if (uid_message && verbose)
+ {
+ g_print ("Modified UID: %s", uid_message);
+ }
+
+ g_free (nolock_reason);
+ g_free (orig_uid);
+ g_free (uid_message);
+
+ gs_profile_end (NULL);
+
+ return ret;
+}
+
+
+/*
+ * Copyright (c) 1991-2004 Jamie Zawinski <[email protected]>
+ * Copyright (c) 2005 William Jon McCann <[email protected]>
+ *
+ * Figure out what locking mechanisms are supported.
+ */
+static gboolean
+lock_initialization (int *argc,
+ char **argv,
+ char **nolock_reason,
+ gboolean verbose)
+{
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = NULL;
+ }
+
+#ifdef NO_LOCKING
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("not compiled with locking support");
+ }
+
+ return FALSE;
+#else /* !NO_LOCKING */
+
+ /* Finish initializing locking, now that we're out of privileged code. */
+ if (! gs_auth_init ())
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("error getting password");
+ }
+
+ return FALSE;
+ }
+
+ /* If locking is currently enabled, but the environment indicates that
+ we have been launched as MDM's "Background" program, then disable
+ locking just in case.
+ */
+ if (getenv ("RUNNING_UNDER_MDM"))
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("running under MDM");
+ }
+
+ return FALSE;
+ }
+
+ /* If the server is XDarwin (MacOS X) then disable locking.
+ (X grabs only affect X programs, so you can use Command-Tab
+ to bring any other Mac program to the front, e.g., Terminal.)
+ */
+ {
+ gboolean macos = FALSE;
+
+#ifdef __APPLE__
+ /* Disable locking if *running* on Apple hardware, since we have no
+ reliable way to determine whether the server is running on MacOS.
+ Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
+ but I'm not really sure about that.
+ */
+ macos = TRUE;
+#endif
+
+ if (macos)
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("Cannot lock securely on MacOS X");
+ }
+
+ return FALSE;
+ }
+ }
+
+#endif /* NO_LOCKING */
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ char *nolock_reason = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! g_thread_supported ())
+ {
+ g_thread_init (NULL);
+ }
+
+ g_type_init ();
+
+ gs_profile_start (NULL);
+
+ if (! privileged_initialization (&argc, argv, verbose))
+ {
+ response_lock_init_failed ();
+ exit (1);
+ }
+
+ error = NULL;
+ if (! gtk_init_with_args (&argc, &argv, NULL, entries, NULL, &error))
+ {
+ if (error != NULL)
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ }
+ exit (1);
+ }
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ if (! lock_initialization (&argc, argv, &nolock_reason, verbose))
+ {
+ if (nolock_reason != NULL)
+ {
+ g_debug ("Screen locking disabled: %s", nolock_reason);
+ g_free (nolock_reason);
+ }
+ response_lock_init_failed ();
+ exit (1);
+ }
+
+ gs_debug_init (verbose, FALSE);
+
+ g_idle_add ((GSourceFunc)popup_dialog_idle, NULL);
+
+ gtk_main ();
+
+ gs_profile_end (NULL);
+ gs_debug_shutdown ();
+
+ return 0;
+}