diff options
author | Ray Strode <rstrode@redhat.com> | 2011-05-26 10:16:51 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2011-08-28 23:35:30 -0400 |
commit | 7e132890463a0ffe6a351f88bada8ee9dda5b863 (patch) | |
tree | f0194525838b15acd9437a823db399d22f3288e8 | |
parent | dfe376a147b9b69072926581659f87f15a7e8436 (diff) | |
download | gdm-7e132890463a0ffe6a351f88bada8ee9dda5b863.tar.gz |
greeter: export library for being a greeter
This will make it easier for people to write external greeters.
Of course, there are still no guarantees about interface stability.
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | gui/Makefile.am | 1 | ||||
-rw-r--r-- | gui/libgdmgreeter/Makefile.am | 78 | ||||
-rw-r--r-- | gui/libgdmgreeter/gdm-greeter-client.c | 1243 | ||||
-rw-r--r-- | gui/libgdmgreeter/gdm-greeter-client.h | 126 | ||||
-rw-r--r-- | gui/libgdmgreeter/gdm-greeter-sessions.c | 280 | ||||
-rw-r--r-- | gui/libgdmgreeter/gdm-greeter-sessions.h | 37 | ||||
-rw-r--r-- | gui/libgdmgreeter/gdmgreeter.pc.in | 10 |
8 files changed, 1785 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac index 394d1759..fa05acb9 100644 --- a/configure.ac +++ b/configure.ac @@ -194,6 +194,14 @@ AC_PATH_PROG(GCONFTOOL, gconftool-2) AM_GCONF_SOURCE_2 +LIBGDMGREETER_LIBS="$SIMPLE_GREETER_LIBS" +AC_SUBST(LIBGDMGREETER_LIBS) +LIBGDMGREETER_CFLAGS="$SIMPLE_GREETER_CFLAGS" +AC_SUBST(LIBGDMGREETER_CFLAGS) +GOBJECT_INTROSPECTION_CHECK([0.9.12]) +LIBGDMGREETER_GIR_INCLUDES="GLib-2.0 GObject-2.0 DBusGLib-1.0" +AC_SUBST(LIBGDMGREETER_GIR_INCLUDES) + dnl --------------------------------------------------------------------------- dnl - Configuration file stuff dnl --------------------------------------------------------------------------- @@ -1421,6 +1429,8 @@ Makefile daemon/Makefile docs/Makefile gui/Makefile +gui/libgdmgreeter/Makefile +gui/libgdmgreeter/gdmgreeter.pc gui/simple-greeter/Makefile gui/simple-greeter/libgdmsimplegreeter/Makefile gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc diff --git a/gui/Makefile.am b/gui/Makefile.am index bda3f7a7..d14ed464 100644 --- a/gui/Makefile.am +++ b/gui/Makefile.am @@ -1,6 +1,7 @@ NULL = SUBDIRS = \ + libgdmgreeter \ simple-greeter \ $(NULL) diff --git a/gui/libgdmgreeter/Makefile.am b/gui/libgdmgreeter/Makefile.am new file mode 100644 index 00000000..f1e2a516 --- /dev/null +++ b/gui/libgdmgreeter/Makefile.am @@ -0,0 +1,78 @@ +END_OF_LIST = + +BUILT_SOURCES = $(END_OF_LIST) +CLEANFILES = $(END_OF_LIST) + +AM_CPPFLAGS = \ + -I. \ + -I.. \ + -DG_LOG_DOMAIN=\"GdmGreeter\" \ + -DDMCONFDIR=\""$(dmconfdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + $(END_OF_LIST) + +lib_LTLIBRARIES = \ + libgdmgreeter.la \ + $(END_OF_LIST) + +libgdmgreeterdir = $(includedir)/gdm/greeter +libgdmgreeter_HEADERS = \ + gdm-greeter-client.h \ + gdm-greeter-sessions.h \ + $(END_OF_LIST) + +libgdmgreeter_la_CFLAGS = \ + $(LIBGDMGREETER_CFLAGS) \ + $(END_OF_LIST) +libgdmgreeter_la_LDFLAGS = \ + -export-symbols-regex '^[^_].*' \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -no-undefined \ + $(END_OF_LIST) + +libgdmgreeter_la_LIBADD = \ + $(LIBGDMGREETER_LIBS) \ + $(END_OF_LIST) + +libgdmgreeter_la_SOURCES = \ + $(libgdmgreeter_HEADERS) \ + gdm-greeter-client.c \ + gdm-greeter-sessions.c \ + $(END_OF_LIST) + +pkgconfigdir = $(libdir)/pkgconfig +dist_pkgconfig_DATA = gdmgreeter.pc + +-include $(INTROSPECTION_MAKEFILE) + +if HAVE_INTROSPECTION +girdir = $(datadir)/gir-1.0 +gir_DATA = GdmGreeter-1.0.gir +typelibsdir = $(libdir)/girepository-1.0 +typelibs_DATA = $(gir_DATA:.gir=.typelib) +INTROSPECTION_GIRS = $(gir_DATA) + +GdmGreeter-1.0.gir: \ + $(INTROSPECTION_SCANNER) \ + libgdmgreeter.la \ + Makefile \ + $(END_OF_LIST) + +GdmGreeter_1_0_gir_SCANNERFLAGS = \ + --warn-all \ + --namespace=GdmGreeter \ + --identifier-prefix GdmGreeter \ + $(libgdmgreeter_la_CFLAGS) \ + $(END_OF_LIST) + +GdmGreeter_1_0_gir_INCLUDES = $(LIBGDMGREETER_GIR_INCLUDES) +GdmGreeter_1_0_gir_LIBS = libgdmgreeter.la +GdmGreeter_1_0_gir_FILES = \ + $(filter-out %-private.h, $(libgdmgreeter_la_SOURCES)) \ + $(BUILT_SOURCES) \ + $(END_OF_LIST) + +CLEANFILES += $(gir_DATA) $(typelibs_DATA) +endif + +MAINTAINERCLEANFILES = Makefile.in diff --git a/gui/libgdmgreeter/gdm-greeter-client.c b/gui/libgdmgreeter/gdm-greeter-client.c new file mode 100644 index 00000000..6cf5b588 --- /dev/null +++ b/gui/libgdmgreeter/gdm-greeter-client.c @@ -0,0 +1,1243 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * 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. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "gdm-greeter-client.h" + +#define GDM_GREETER_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_CLIENT, GdmGreeterClientPrivate)) + +#define GREETER_SERVER_DBUS_PATH "/org/gnome/DisplayManager/GreeterServer" +#define GREETER_SERVER_DBUS_INTERFACE "org.gnome.DisplayManager.GreeterServer" + +#define GDM_DBUS_NAME "org.gnome.DisplayManager" +#define GDM_DBUS_DISPLAY_INTERFACE "org.gnome.DisplayManager.Display" + +struct GdmGreeterClientPrivate +{ + DBusConnection *connection; + char *address; + + char *display_id; + gboolean display_is_local; + + guint32 is_connected : 1; +}; + +enum { + PROP_0, + PROP_DISPLAY_IS_LOCAL +}; + +enum { + INFO, + PROBLEM, + INFO_QUERY, + SECRET_INFO_QUERY, + SERVICE_UNAVAILABLE, + READY, + CONVERSATION_STOPPED, + RESET, + AUTHENTICATION_FAILED, + SELECTED_USER_CHANGED, + DEFAULT_SESSION_CHANGED, + TIMED_LOGIN_REQUESTED, + SESSION_OPENED, + LAST_SIGNAL +}; + +static guint gdm_greeter_client_signals [LAST_SIGNAL]; + +static void gdm_greeter_client_class_init (GdmGreeterClientClass *klass); +static void gdm_greeter_client_init (GdmGreeterClient *greeter_client); +static void gdm_greeter_client_finalize (GObject *object); + +G_DEFINE_TYPE (GdmGreeterClient, gdm_greeter_client, G_TYPE_OBJECT) + +static gpointer client_object = NULL; + +GQuark +gdm_greeter_client_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) + error_quark = g_quark_from_static_string ("gdm-greeter-client"); + + return error_quark; +} + +/** + * gdm_greeter_client_get_display_is_local: + * + * @client: a #GdmGreeterClient + * + * Returns: %TRUE if display is local display + */ +gboolean +gdm_greeter_client_get_display_is_local (GdmGreeterClient *client) +{ + g_return_val_if_fail (GDM_IS_GREETER_CLIENT (client), FALSE); + + return client->priv->display_is_local; +} + +static void +emit_string_and_int_signal_for_message (GdmGreeterClient *client, + const char *name, + DBusMessage *message, + int signal) +{ + DBusError error; + const char *text; + int number; + dbus_bool_t res; + + dbus_error_init (&error); + res = dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, &text, + DBUS_TYPE_INT32, &number, + DBUS_TYPE_INVALID); + if (res) { + + g_debug ("GdmGreeterClient: Received %s (%s %d)", name, text, number); + + g_signal_emit (client, + gdm_greeter_client_signals[signal], + 0, text, number); + } else { + g_warning ("Unable to get arguments: %s", error.message); + dbus_error_free (&error); + } +} + +static void +emit_string_and_string_signal_for_message (GdmGreeterClient *client, + const char *name, + DBusMessage *message, + int signal) +{ + DBusError error; + char *text1; + char *text2; + dbus_bool_t res; + + dbus_error_init (&error); + res = dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, &text1, + DBUS_TYPE_STRING, &text2, + DBUS_TYPE_INVALID); + if (res) { + + g_debug ("GdmGreeterClient: Received %s (%s, %s)", name, text1, text2); + + g_signal_emit (client, + gdm_greeter_client_signals[signal], + 0, text1, text2); + } else { + g_warning ("Unable to get arguments: %s", error.message); + dbus_error_free (&error); + } + dbus_error_free (&error); +} + +static void +emit_string_signal_for_message (GdmGreeterClient *client, + const char *name, + DBusMessage *message, + int signal) +{ + DBusError error; + const char *text; + dbus_bool_t res; + + dbus_error_init (&error); + res = dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, &text, + DBUS_TYPE_INVALID); + if (res) { + + g_debug ("GdmGreeterClient: Received %s (%s)", name, text); + + g_signal_emit (client, + gdm_greeter_client_signals[signal], + 0, text); + } else { + g_warning ("Unable to get arguments: %s", error.message); + dbus_error_free (&error); + } +} + +static void +on_selected_user_changed (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_signal_for_message (client, "SelectedUserChanged", message, SELECTED_USER_CHANGED); +} + +static void +on_default_session_changed (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_signal_for_message (client, "DefaultSessionNameChanged", message, DEFAULT_SESSION_CHANGED); +} + +static void +on_timed_login_requested (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_and_int_signal_for_message (client, "TimedLoginRequested", message, TIMED_LOGIN_REQUESTED); +} + +static void +on_session_opened (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_signal_for_message (client, "SessionOpened", message, SESSION_OPENED); +} + +static void +on_info_query (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_and_string_signal_for_message (client, "InfoQuery", message, INFO_QUERY); +} + +static void +on_secret_info_query (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_and_string_signal_for_message (client, "SecretInfoQuery", message, SECRET_INFO_QUERY); +} + +static void +on_info (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_and_string_signal_for_message (client, "Info", message, INFO); +} + +static void +on_problem (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_and_string_signal_for_message (client, "Problem", message, PROBLEM); +} + +static void +on_service_unavailable (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_signal_for_message (client, "ServiceUnavailable", message, SERVICE_UNAVAILABLE); +} + +static void +on_ready (GdmGreeterClient *client, + DBusMessage *message) +{ + emit_string_signal_for_message (client, "Ready", message, READY); +} + +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) +{ + g_debug ("GdmGreeterClient: Reset"); + + g_signal_emit (client, + gdm_greeter_client_signals[RESET], + 0); +} + +static void +on_authentication_failed (GdmGreeterClient *client, + DBusMessage *message) +{ + g_debug ("GdmGreeterClient: Authentication failed"); + + g_signal_emit (client, + gdm_greeter_client_signals[AUTHENTICATION_FAILED], + 0); +} + +static gboolean +send_dbus_string_method (DBusConnection *connection, + const char *method, + const char *payload) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + const char *str; + + if (payload != NULL) { + str = payload; + } else { + str = ""; + } + + g_debug ("GdmGreeterClient: Calling %s", method); + message = dbus_message_new_method_call (NULL, + GREETER_SERVER_DBUS_PATH, + GREETER_SERVER_DBUS_INTERFACE, + method); + if (message == NULL) { + g_warning ("Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &str); + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &error); + + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) { + g_warning ("%s %s raised: %s\n", + method, + error.name, + error.message); + return FALSE; + } + if (reply != NULL) { + dbus_message_unref (reply); + } + dbus_connection_flush (connection); + + return TRUE; +} + +static gboolean +send_dbus_string_and_bool_method (DBusConnection *connection, + const char *method, + const char *string_payload, + gboolean bool_payload) +{ + DBusMessage *message; + DBusMessageIter iter; + const char *str; + + if (string_payload != NULL) { + str = string_payload; + } else { + str = ""; + } + + g_debug ("GdmGreeterClient: Calling %s", method); + message = dbus_message_new_method_call (NULL, + GREETER_SERVER_DBUS_PATH, + GREETER_SERVER_DBUS_INTERFACE, + method); + if (message == NULL) { + g_warning ("Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &str); + + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_BOOLEAN, + &bool_payload); + dbus_message_set_no_reply (message, TRUE); + + dbus_connection_send (connection, message, NULL); + + dbus_message_unref (message); + + dbus_connection_flush (connection); + + return TRUE; +} + +static gboolean +send_dbus_string_and_string_method (DBusConnection *connection, + const char *method, + const char *payload1, + const char *payload2) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + const char *str; + + g_debug ("GdmGreeterClient: Calling %s", method); + message = dbus_message_new_method_call (NULL, + GREETER_SERVER_DBUS_PATH, + GREETER_SERVER_DBUS_INTERFACE, + method); + if (message == NULL) { + g_warning ("Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_message_iter_init_append (message, &iter); + + if (payload1 != NULL) { + str = payload1; + } else { + str = ""; + } + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &str); + + if (payload2 != NULL) { + str = payload2; + } else { + str = ""; + } + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &str); + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &error); + + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) { + g_warning ("%s %s raised: %s\n", + method, + error.name, + error.message); + return FALSE; + } + if (reply != NULL) { + dbus_message_unref (reply); + } + dbus_connection_flush (connection); + + return TRUE; +} + +static gboolean +send_dbus_void_method (DBusConnection *connection, + const char *method) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + + g_debug ("GdmGreeterClient: Calling %s", method); + message = dbus_message_new_method_call (NULL, + GREETER_SERVER_DBUS_PATH, + GREETER_SERVER_DBUS_INTERFACE, + method); + if (message == NULL) { + g_warning ("Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &error); + + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) { + g_warning ("%s %s raised: %s\n", + method, + error.name, + error.message); + return FALSE; + } + if (reply != NULL) { + dbus_message_unref (reply); + } + dbus_connection_flush (connection); + + return TRUE; +} + +/** + * gdm_greeter_call_start_conversation: + * + * @client: a #GdmGreeterClient + * @service_name: PAM service name + * + * Tells GDM to prepare to start a PAM conversation with + * the specified PAM service. + * + * Normally, this would get called when the greeter first + * starts up, either one call to the "gdm" service or one + * for each auth method (say "gdm-password", "gdm-fingerprint", + * "gdm-smartcard") + * + * After the conversation has been started, the ready signal + * will be emitted. + */ +void +gdm_greeter_client_call_start_conversation (GdmGreeterClient *client, + const char *service_name) +{ + send_dbus_string_method (client->priv->connection, + "StartConversation", service_name); +} + +/** + * gdm_greeter_call_begin_auto_login: + * + * @client: a #GdmGreeterClient + * @username: user to auto login or %NULL to use the configured default + * + * Tells GDM to log the user in without first authenticating. + * + * Normally, this would get called after a timed-login-requested + * signal once the time out has been reached. + */ +void +gdm_greeter_client_call_begin_auto_login (GdmGreeterClient *client, + const char *username) +{ + if (username == NULL) { + username = "__auto"; + } + + send_dbus_string_method (client->priv->connection, + "BeginAutoLogin", username); +} + +/** + * gdm_greeter_call_begin_verification: + * + * @client: a #GdmGreeterClient + * @service_name: PAM service name + * + * Tells GDM to start the PAM authentication process with no specified + * user name. + * + * This should only be called after the ready signal is emitted for + * @service_name. + * + * Normally, this would get called if the greeter has no user list, or + * if the user clicks "Other" or "Not Listed?" in the user list. + */ +void +gdm_greeter_client_call_begin_verification (GdmGreeterClient *client, + const char *service_name) +{ + send_dbus_string_method (client->priv->connection, + "BeginVerification", service_name); +} + +/** + * gdm_greeter_call_begin_verification_for_user: + * + * @client: a #GdmGreeterClient + * @service_name: PAM service name + * @username: user to start verification process for + * + * Tells GDM to start the PAM authentication process with the @username + * user name. + * + * This should only be called after the ready signal is emitted for + * @service_name. + * + * Normally, this would get called in reponse to a user getting selected + * from the user list. If the greeter doesn't have a user list, it's + * better to call gdm_greeter_client_call_begin_verification() and let + * the PAM service handle asking for a username, than using this function. + */ +void +gdm_greeter_client_call_begin_verification_for_user (GdmGreeterClient *client, + const char *service_name, + const char *username) +{ + send_dbus_string_and_string_method (client->priv->connection, + "BeginVerificationForUser", + service_name, + username); +} + +/** + * gdm_greeter_call_answer_query: + * + * @client: a #GdmGreeterClient + * @service_name: PAM service name + * @answer: The answer to the currently pending PAM question. + * + * Responds to GDM's pending request for information. + * + * This should only be called after the info-query or secret-info-query signal is + * emitted for * @service_name. + * + * Normally, this would get called after the user types in an answer. + */ +void +gdm_greeter_client_call_answer_query (GdmGreeterClient *client, + const char *service_name, + const char *answer) +{ + send_dbus_string_and_string_method (client->priv->connection, + "AnswerQuery", + service_name, + answer); +} + +/** + * gdm_greeter_call_start_session_when_ready: + * + * @client: a #GdmGreeterClient + * @service_name: PAM service name + * @should_start_session: whether to block the login process + * + * After the user has successfully interacted with PAM, GDM waits + * for a final go ahead before finishing the login process and starting the + * users session. + * + * This call gives that go ahead. + * + * Normally, you would call this after the user has had a chance to interact + * with any important, non-login related information on the login screen + * (like a session selector). + */ +void +gdm_greeter_client_call_start_session_when_ready (GdmGreeterClient *client, + const char *service_name, + gboolean should_start_session) +{ + send_dbus_string_and_bool_method (client->priv->connection, + "StartSessionWhenReady", + service_name, + should_start_session); +} + +/** + * gdm_greeter_call_select_session: + * + * @client: a #GdmGreeterClient + * @session: session from /usr/share/xsessions to start + * + * Select a session for the user to login with. + * + * Normally, you would call this if the user selects a specific session in + * a list on screen somewhere. + */ +void +gdm_greeter_client_call_select_session (GdmGreeterClient *client, + const char *session) +{ + send_dbus_string_method (client->priv->connection, + "SelectSession", + session); +} + +/** + * gdm_greeter_call_select_user: + * + * @client: a #GdmGreeterClient + * @user: a user name + * + * Select a user to login with. + * + * Normally, you would call this if the user selects a specific session in + * a list on screen somewhere. + */ +void +gdm_greeter_client_call_select_user (GdmGreeterClient *client, + const char *user) +{ + send_dbus_string_method (client->priv->connection, + "SelectUser", + user); +} + +/** + * gdm_greeter_call_select_hostname: + * + * @client: a #GdmGreeterClient + * @hostname: a hostname + * + * Select a user to login with. + * + * Normally, you would call this from a chooser to select a specific host. + */ +void +gdm_greeter_client_call_select_hostname (GdmGreeterClient *client, + const char *hostname) +{ + send_dbus_string_method (client->priv->connection, + "SelectHostname", + hostname); +} + +/** + * gdm_greeter_call_canel: + * + * @client: a #GdmGreeterClient + * + * Cancel all pending PAM conversations. + * + * Normally you would call this if the user hits a Cancel button when + * asked a question they don't want to answer. + */ +void +gdm_greeter_client_call_cancel (GdmGreeterClient *client) +{ + send_dbus_void_method (client->priv->connection, + "Cancel"); +} + +/** + * gdm_greeter_call_disconnect: + * + * @client: a #GdmGreeterClient + * + * Disconnect from gdm slave. + * + * You shouldn't call this unless the connection + * is remote. + */ +void +gdm_greeter_client_call_disconnect (GdmGreeterClient *client) +{ + g_return_if_fail (gdm_greeter_client_get_display_is_local (client) == FALSE); + + send_dbus_void_method (client->priv->connection, + "Disconnect"); +} + +static gboolean +send_get_display_id (GdmGreeterClient *client, + const char *method, + char **answerp) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + gboolean ret; + const char *answer; + + ret = FALSE; + + g_debug ("GdmGreeterClient: Calling %s", method); + message = dbus_message_new_method_call (NULL, + GREETER_SERVER_DBUS_PATH, + GREETER_SERVER_DBUS_INTERFACE, + method); + if (message == NULL) { + g_warning ("Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (client->priv->connection, + message, + -1, + &error); + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) { + g_warning ("%s %s raised: %s\n", + method, + error.name, + error.message); + goto out; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &answer); + if (answerp != NULL) { + *answerp = g_strdup (answer); + } + ret = TRUE; + + dbus_message_unref (reply); + dbus_connection_flush (client->priv->connection); + + out: + + return ret; +} + + +static char * +gdm_greeter_client_call_get_display_id (GdmGreeterClient *client) +{ + char *display_id; + + display_id = NULL; + send_get_display_id (client, + "GetDisplayId", + &display_id); + + return display_id; +} + +static void +cache_display_values (GdmGreeterClient *client) +{ + DBusGProxy *display_proxy; + DBusGConnection *connection; + GError *error; + gboolean res; + + g_free (client->priv->display_id); + client->priv->display_id = gdm_greeter_client_call_get_display_id (client); + if (client->priv->display_id == NULL) { + return; + } + + error = NULL; + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + return; + } + + g_debug ("GdmGreeterClient: Creating proxy for %s", client->priv->display_id); + display_proxy = dbus_g_proxy_new_for_name (connection, + GDM_DBUS_NAME, + client->priv->display_id, + GDM_DBUS_DISPLAY_INTERFACE); + /* cache some values up front */ + error = NULL; + res = dbus_g_proxy_call (display_proxy, + "IsLocal", + &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &client->priv->display_is_local, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to get value: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to get value"); + } + } +} + +static DBusHandlerResult +client_dbus_handle_message (DBusConnection *connection, + DBusMessage *message, + void *user_data, + dbus_bool_t local_interface) +{ + GdmGreeterClient *client = GDM_GREETER_CLIENT (user_data); + + g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + + if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "InfoQuery")) { + on_info_query (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "SecretInfoQuery")) { + on_secret_info_query (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Info")) { + on_info (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Problem")) { + on_problem (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "ServiceUnavailable")) { + 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")) { + on_authentication_failed (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "SelectedUserChanged")) { + on_selected_user_changed (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "DefaultSessionNameChanged")) { + on_default_session_changed (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "TimedLoginRequested")) { + on_timed_login_requested (client, message); + } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "SessionOpened")) { + on_session_opened (client, message); + } else { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +client_dbus_filter_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GdmGreeterClient *client = GDM_GREETER_CLIENT (user_data); + const char *path; + + path = dbus_message_get_path (message); + + g_debug ("GdmGreeterClient: obj_path=%s interface=%s method=%s", + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message)); + + if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") + && strcmp (path, DBUS_PATH_LOCAL) == 0) { + + g_message ("Got disconnected from the session message bus"); + + dbus_connection_unref (connection); + client->priv->connection = NULL; + + } else if (dbus_message_is_signal (message, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) { + g_debug ("GdmGreeterClient: Name owner changed?"); + } else { + return client_dbus_handle_message (connection, message, user_data, FALSE); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +/** + * gdm_greeter_client_open_connection: + * + * @client: a #GdmGreeterClient + * + * Initiates a connection to GDM daemon "slave" process. + * + * This function should be called before doing other calls. + * + * Returns: %TRUE if connected, or %FALSE if unavailable + */ +gboolean +gdm_greeter_client_open_connection (GdmGreeterClient *client, + GError **error) +{ + DBusError local_error; + + g_return_val_if_fail (GDM_IS_GREETER_CLIENT (client), FALSE); + g_return_val_if_fail (!client->priv->is_connected, TRUE); + + if (client->priv->address == NULL) { + g_warning ("GDM_GREETER_DBUS_ADDRESS not set"); + g_set_error (error, + GDM_GREETER_CLIENT_ERROR, + GDM_GREETER_CLIENT_ERROR_GENERIC, + "GDM_GREETER_DBUS_ADDRESS not set"); + goto out; + } + + g_debug ("GdmGreeterClient: connecting to address: %s", client->priv->address); + + dbus_error_init (&local_error); + client->priv->connection = dbus_connection_open (client->priv->address, &local_error); + if (client->priv->connection == NULL) { + if (dbus_error_is_set (&local_error)) { + g_warning ("error opening connection: %s", local_error.message); + g_set_error (error, + GDM_GREETER_CLIENT_ERROR, + GDM_GREETER_CLIENT_ERROR_GENERIC, + "%s", local_error.message); + dbus_error_free (&local_error); + } else { + g_warning ("Unable to open connection"); + } + goto out; + } + + dbus_connection_setup_with_g_main (client->priv->connection, NULL); + dbus_connection_set_exit_on_disconnect (client->priv->connection, TRUE); + + dbus_connection_add_filter (client->priv->connection, + client_dbus_filter_function, + client, + NULL); + + cache_display_values (client); + + + client->priv->is_connected = TRUE; + + out: + return client->priv->is_connected; +} + +static void +gdm_greeter_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +gdm_greeter_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmGreeterClient *self; + + self = GDM_GREETER_CLIENT (object); + + switch (prop_id) { + case PROP_DISPLAY_IS_LOCAL: + g_value_set_boolean (value, self->priv->display_is_local); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_greeter_client_class_init (GdmGreeterClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_greeter_client_get_property; + object_class->set_property = gdm_greeter_client_set_property; + object_class->finalize = gdm_greeter_client_finalize; + + g_type_class_add_private (klass, sizeof (GdmGreeterClientPrivate)); + + gdm_greeter_client_signals[INFO_QUERY] = + g_signal_new ("info-query", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmGreeterClientClass, info_query), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, + G_TYPE_STRING, G_TYPE_STRING); + + gdm_greeter_client_signals[SECRET_INFO_QUERY] = + g_signal_new ("secret-info-query", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmGreeterClientClass, secret_info_query), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, + G_TYPE_STRING, G_TYPE_STRING); + + gdm_greeter_client_signals[INFO] = + g_signal_new ("info", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, info), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, + G_TYPE_STRING, G_TYPE_STRING); + + gdm_greeter_client_signals[PROBLEM] = + g_signal_new ("problem", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, problem), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, + G_TYPE_STRING, G_TYPE_STRING); + + gdm_greeter_client_signals[SERVICE_UNAVAILABLE] = + g_signal_new ("service-unavailable", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, service_unavailable), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 1, G_TYPE_STRING); + + gdm_greeter_client_signals[READY] = + g_signal_new ("ready", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, ready), + NULL, + NULL, + g_cclosure_marshal_generic, + 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_generic, + G_TYPE_NONE, + 1, G_TYPE_STRING); + + gdm_greeter_client_signals[RESET] = + g_signal_new ("reset", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, reset), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 0); + gdm_greeter_client_signals[AUTHENTICATION_FAILED] = + g_signal_new ("authentication-failed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, authentication_failed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 0); + gdm_greeter_client_signals[SELECTED_USER_CHANGED] = + g_signal_new ("selected-user-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, selected_user_changed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, G_TYPE_STRING); + gdm_greeter_client_signals[DEFAULT_SESSION_CHANGED] = + g_signal_new ("default-session-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, default_session_changed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, G_TYPE_STRING); + gdm_greeter_client_signals[TIMED_LOGIN_REQUESTED] = + g_signal_new ("timed-login-requested", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, timed_login_requested), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, G_TYPE_STRING, G_TYPE_INT); + + gdm_greeter_client_signals[SESSION_OPENED] = + g_signal_new ("session-opened", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmGreeterClientClass, session_opened), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_DISPLAY_IS_LOCAL, + g_param_spec_boolean ("display-is-local", + "display is local", + "display is local", + FALSE, + G_PARAM_READABLE)); +} + +static void +gdm_greeter_client_init (GdmGreeterClient *client) +{ + + client->priv = GDM_GREETER_CLIENT_GET_PRIVATE (client); + + client->priv->address = g_strdup (g_getenv ("GDM_GREETER_DBUS_ADDRESS")); +} + +static void +gdm_greeter_client_finalize (GObject *object) +{ + GdmGreeterClient *client; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_GREETER_CLIENT (object)); + + client = GDM_GREETER_CLIENT (object); + + g_return_if_fail (client->priv != NULL); + + g_free (client->priv->address); + + G_OBJECT_CLASS (gdm_greeter_client_parent_class)->finalize (object); +} + +GdmGreeterClient * +gdm_greeter_client_new (void) +{ + if (client_object != NULL) { + g_object_ref (client_object); + } else { + client_object = g_object_new (GDM_TYPE_GREETER_CLIENT, NULL); + g_object_add_weak_pointer (client_object, + (gpointer *) &client_object); + } + + return GDM_GREETER_CLIENT (client_object); +} diff --git a/gui/libgdmgreeter/gdm-greeter-client.h b/gui/libgdmgreeter/gdm-greeter-client.h new file mode 100644 index 00000000..7fa3aaa7 --- /dev/null +++ b/gui/libgdmgreeter/gdm-greeter-client.h @@ -0,0 +1,126 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __GDM_GREETER_CLIENT_H +#define __GDM_GREETER_CLIENT_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_GREETER_CLIENT (gdm_greeter_client_get_type ()) +#define GDM_GREETER_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_GREETER_CLIENT, GdmGreeterClient)) +#define GDM_GREETER_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_GREETER_CLIENT, GdmGreeterClientClass)) +#define GDM_IS_GREETER_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_GREETER_CLIENT)) +#define GDM_IS_GREETER_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_GREETER_CLIENT)) +#define GDM_GREETER_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_GREETER_CLIENT, GdmGreeterClientClass)) + +typedef struct GdmGreeterClientPrivate GdmGreeterClientPrivate; + +typedef struct +{ + GObject parent; + GdmGreeterClientPrivate *priv; +} GdmGreeterClient; + +typedef struct +{ + GObjectClass parent_class; + + void (* info_query) (GdmGreeterClient *client, + const char *service_name, + const char *query_text); + + void (* secret_info_query) (GdmGreeterClient *client, + const char *service_name, + const char *query_text); + + void (* info) (GdmGreeterClient *client, + const char *service_name, + const char *info); + + void (* problem) (GdmGreeterClient *client, + const char *service_name, + const char *problem); + void (* service_unavailable) (GdmGreeterClient *client, + 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, + const char *username); + void (* default_session_changed) (GdmGreeterClient *client, + const char *session_id); + void (* timed_login_requested) (GdmGreeterClient *client, + const char *username, + int delay); + void (* session_opened) (GdmGreeterClient *client, + const char *service_name); +} GdmGreeterClientClass; + +#define GDM_GREETER_CLIENT_ERROR (gdm_greeter_client_error_quark ()) + +typedef enum _GdmGreeterClientError { + GDM_GREETER_CLIENT_ERROR_GENERIC = 0, +} GdmGreeterClientError; + +GType gdm_greeter_client_get_type (void); +GQuark gdm_greeter_client_error_quark (void); + +GdmGreeterClient * gdm_greeter_client_new (void); + +gboolean gdm_greeter_client_open_connection (GdmGreeterClient *client, + GError **error); +gboolean gdm_greeter_client_get_display_is_local (GdmGreeterClient *client); + +void gdm_greeter_client_call_start_conversation (GdmGreeterClient *client, + const char *service_name); +void gdm_greeter_client_call_begin_auto_login (GdmGreeterClient *client, + const char *username); +void gdm_greeter_client_call_begin_verification (GdmGreeterClient *client, + const char *service_name); +void gdm_greeter_client_call_begin_verification_for_user (GdmGreeterClient *client, + const char *service_name, + const char *username); +void gdm_greeter_client_call_cancel (GdmGreeterClient *client); +void gdm_greeter_client_call_disconnect (GdmGreeterClient *client); +void gdm_greeter_client_call_select_hostname (GdmGreeterClient *client, + const char *text); +void gdm_greeter_client_call_select_user (GdmGreeterClient *client, + const char *text); +void gdm_greeter_client_call_select_language (GdmGreeterClient *client, + const char *text); +void gdm_greeter_client_call_select_session (GdmGreeterClient *client, + const char *text); +void gdm_greeter_client_call_answer_query (GdmGreeterClient *client, + const char *service_name, + const char *text); + +void gdm_greeter_client_call_start_session_when_ready (GdmGreeterClient *client, + const char *service_name, + gboolean should_start_session); + + +G_END_DECLS + +#endif /* __GDM_GREETER_CLIENT_H */ diff --git a/gui/libgdmgreeter/gdm-greeter-sessions.c b/gui/libgdmgreeter/gdm-greeter-sessions.c new file mode 100644 index 00000000..fe342930 --- /dev/null +++ b/gui/libgdmgreeter/gdm-greeter-sessions.c @@ -0,0 +1,280 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2008 Red Hat, Inc, + * 2007 William Jon McCann <mccann@jhu.edu> + * + * 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 : William Jon McCann <mccann@jhu.edu> + * 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 "gdm-greeter-sessions.h" + +typedef struct _GdmSessionFile { + char *id; + char *path; + char *translated_name; + char *translated_comment; +} GdmSessionFile; + +static GHashTable *gdm_available_sessions_map; + +static gboolean gdm_sessions_map_is_initialized = FALSE; + +/* adapted from gnome-menus desktop-entries.c */ +static gboolean +key_file_is_relevant (GKeyFile *key_file) +{ + GError *error; + gboolean no_display; + gboolean hidden; + gboolean tryexec_failed; + char *tryexec; + + error = NULL; + no_display = g_key_file_get_boolean (key_file, + G_KEY_FILE_DESKTOP_GROUP, + "NoDisplay", + &error); + if (error) { + no_display = FALSE; + g_error_free (error); + } + + error = NULL; + hidden = g_key_file_get_boolean (key_file, + G_KEY_FILE_DESKTOP_GROUP, + "Hidden", + &error); + if (error) { + hidden = FALSE; + g_error_free (error); + } + + tryexec_failed = FALSE; + tryexec = g_key_file_get_string (key_file, + G_KEY_FILE_DESKTOP_GROUP, + "TryExec", + NULL); + if (tryexec) { + char *path; + + path = g_find_program_in_path (g_strstrip (tryexec)); + + tryexec_failed = (path == NULL); + + g_free (path); + g_free (tryexec); + } + + if (no_display || hidden || tryexec_failed) { + return FALSE; + } + + return TRUE; +} + +static void +load_session_file (const char *id, + const char *path) +{ + GKeyFile *key_file; + GError *error; + gboolean res; + GdmSessionFile *session; + + key_file = g_key_file_new (); + + error = NULL; + res = g_key_file_load_from_file (key_file, path, 0, &error); + + if (!res) { + g_debug ("Failed to load \"%s\": %s\n", path, error->message); + g_error_free (error); + goto out; + } + + if (! g_key_file_has_group (key_file, G_KEY_FILE_DESKTOP_GROUP)) { + goto out; + } + + res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL); + if (! res) { + g_debug ("\"%s\" contains no \"Name\" key\n", path); + goto out; + } + + if (!key_file_is_relevant (key_file)) { + g_debug ("\"%s\" is hidden or contains non-executable TryExec program\n", path); + goto out; + } + + session = g_new0 (GdmSessionFile, 1); + + session->id = g_strdup (id); + session->path = g_strdup (path); + + session->translated_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL, NULL); + session->translated_comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Comment", NULL, NULL); + + g_hash_table_insert (gdm_available_sessions_map, + g_strdup (id), + session); + out: + g_key_file_free (key_file); +} + +static void +collect_sessions_from_directory (const char *dirname) +{ + GDir *dir; + const char *filename; + + /* FIXME: add file monitor to directory */ + + dir = g_dir_open (dirname, 0, NULL); + if (dir == NULL) { + return; + } + + while ((filename = g_dir_read_name (dir))) { + char *id; + char *full_path; + + if (! g_str_has_suffix (filename, ".desktop")) { + continue; + } + id = g_strndup (filename, strlen (filename) - strlen (".desktop")); + + full_path = g_build_filename (dirname, filename, NULL); + + load_session_file (id, full_path); + + g_free (id); + g_free (full_path); + } + + g_dir_close (dir); +} + +static void +collect_sessions (void) +{ + int i; + const char *search_dirs[] = { + "/etc/X11/sessions/", + DMCONFDIR "/Sessions/", + DATADIR "/gdm/BuiltInSessions/", + DATADIR "/xsessions/", + NULL + }; + + if (gdm_available_sessions_map == NULL) { + gdm_available_sessions_map = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + } + + for (i = 0; search_dirs [i] != NULL; i++) { + collect_sessions_from_directory (search_dirs [i]); + } +} + +/** + * gdm_greeter_get_session_ids: + * + * Reads /usr/share/xsessions and other relevant places for possible sessions + * to log into and returns the complete list. + * + * Returns: (transfer full): a %NULL terminated list of session ids + */ +char ** +gdm_greeter_get_session_ids (void) +{ + GHashTableIter iter; + gpointer key, value; + GPtrArray *array; + + if (!gdm_sessions_map_is_initialized) { + collect_sessions (); + + gdm_sessions_map_is_initialized = TRUE; + } + + array = g_ptr_array_new (); + g_hash_table_iter_init (&iter, gdm_available_sessions_map); + while (g_hash_table_iter_next (&iter, &key, &value)) { + GdmSessionFile *session; + + session = (GdmSessionFile *) value; + + g_ptr_array_add (array, g_strdup (session->id)); + } + g_ptr_array_add (array, NULL); + + return (char **) g_ptr_array_free (array, FALSE); +} + +/** + * gdm_greeter_get_session_name_and_description: + * @id: an id from gdm_greeter_get_session_ids() + * @description: (out): optional returned session description + * + * Takes an xsession id and returns the name and comment about it. + * + * Returns: The session name if found, or %NULL otherwise + */ +char * +gdm_greeter_get_session_name_and_description (const char *id, + char **description) +{ + GdmSessionFile *session; + char *name; + + if (!gdm_sessions_map_is_initialized) { + collect_sessions (); + + gdm_sessions_map_is_initialized = TRUE; + } + + session = (GdmSessionFile *) g_hash_table_lookup (gdm_available_sessions_map, + id); + + if (session == NULL) { + return NULL; + } + + name = g_strdup (session->translated_name); + + if (description != NULL) { + *description = g_strdup (session->translated_comment); + } + + return name; +} diff --git a/gui/libgdmgreeter/gdm-greeter-sessions.h b/gui/libgdmgreeter/gdm-greeter-sessions.h new file mode 100644 index 00000000..ab78a985 --- /dev/null +++ b/gui/libgdmgreeter/gdm-greeter-sessions.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2008 Red Hat, Inc. + * Copyright 2007 William Jon McCann <mccann@jhu.edu> + * + * 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 + * William Jon McCann + */ + +#ifndef __GDM_GREETER_SESSIONS_H +#define __GDM_GREETER_SESSIONS_H + +#include <glib.h> + +G_BEGIN_DECLS + +char ** gdm_greeter_get_session_ids (void); +char * gdm_greeter_get_session_name_and_description (const char *id, + char **description); + +G_END_DECLS + +#endif /* __GDM_SESSION_H */ diff --git a/gui/libgdmgreeter/gdmgreeter.pc.in b/gui/libgdmgreeter/gdmgreeter.pc.in new file mode 100644 index 00000000..b1c68022 --- /dev/null +++ b/gui/libgdmgreeter/gdmgreeter.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GDM Greeter Library +Description: Client Library for communicating with GDM greeter server +Version: @VERSION@ +Libs: -L${libdir} -lgdmgreeter +Cflags: -I${includedir}/gdm/greeter |