From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../liblightdm-gobject-1.5.0/Makefile.kmk | 61 + .../liblightdm-gobject-1.5.0/config.h | 90 ++ .../liblightdm-gobject-1.5.0/greeter.c | 1442 +++++++++++++++++ .../liblightdm-gobject-1.5.0/language.c | 416 +++++ .../liblightdm-gobject-1.5.0/layout.c | 344 ++++ .../liblightdm-gobject-1.5.0/power.c | 211 +++ .../liblightdm-gobject-1.5.0/session.c | 388 +++++ .../liblightdm-gobject-1.5.0/system.c | 42 + .../liblightdm-gobject-1.5.0/user.c | 1655 ++++++++++++++++++++ 9 files changed, 4649 insertions(+) create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c create mode 100644 src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c (limited to 'src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0') diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk new file mode 100644 index 00000000..dfae0972 --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk @@ -0,0 +1,61 @@ +# $Id: Makefile.kmk $ +## @file +# Makefile for liblighdm-gobject 1.5.0 +# + +# +# Copyright (C) 2013-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# 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, in version 3 of the +# License. +# +# 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, see . +# +# SPDX-License-Identifier: GPL-3.0-only +# + +SUB_DEPTH = ../../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# The greeter module. +LIBRARIES += VBox-liblightdm-gobject + +VBox-liblightdm-gobject_TEMPLATE = VBoxGuestR3Lib +VBox-liblightdm-gobject_SDKS = VBoxGlib20WithIo +VBox-liblightdm-gobject_INCS = \ + /usr/include/glib-2.0 \ + /usr/lib/i386-linux-gnu/glib-2.0/include \ + /usr/lib/x86_64-linux-gnu/glib-2.0/include \ + /usr/include/gio-unix-2.0 +ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING + VBox-liblightdm-gobject_DEFS = \ + CONFIG_DIR="/etc/lightdm" \ + XSESSIONS_DIR="/usr/share/xsessions" \ + REMOTE_SESSIONS_DIR="/usr/share/lightdm/remote-sessions" +else + VBox-liblightdm-gobject_DEFS = \ + CONFIG_DIR=\"/etc/lightdm\" \ + XSESSIONS_DIR=\"/usr/share/xsessions\" \ + REMOTE_SESSIONS_DIR=\"/usr/share/lightdm/remote-sessions\" +endif +VBox-liblightdm-gobject_SOURCES = \ + greeter.c \ + language.c \ + layout.c \ + power.c \ + session.c \ + system.c \ + user.c + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h new file mode 100644 index 00000000..c06fc08d --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h @@ -0,0 +1,90 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Gettext package */ +#define GETTEXT_PACKAGE "lightdm" + +/* Greeter session */ +#define GREETER_SESSION "default" + +/* User to run greeter as */ +#define GREETER_USER "lightdm" + +/* Define to 1 if you have the `clearenv' function. */ +#define HAVE_CLEARENV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SECURITY_PAM_APPL_H 1 + +/* Define to 1 if you have the `setresgid' function. */ +#define HAVE_SETRESGID 1 + +/* Define to 1 if you have the `setresuid' function. */ +#define HAVE_SETRESUID 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Name of package */ +#define PACKAGE "lightdm" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "lightdm" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "lightdm 1.5.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "lightdm" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.5.0" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* User session */ +#define USER_SESSION "default" + +/* Version number of package */ +#define VERSION "1.5.0" diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c new file mode 100644 index 00000000..3d44b562 --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c @@ -0,0 +1,1442 @@ +/* + * Copyright (C) 2010 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include "config.h" + +#include +#include +#include + +#include "lightdm/greeter.h" + +enum { + PROP_0, + PROP_DEFAULT_SESSION_HINT, + PROP_HIDE_USERS_HINT, + PROP_SHOW_MANUAL_LOGIN_HINT, + PROP_SHOW_REMOTE_LOGIN_HINT, + PROP_LOCK_HINT, + PROP_HAS_GUEST_ACCOUNT_HINT, + PROP_SELECT_USER_HINT, + PROP_SELECT_GUEST_HINT, + PROP_AUTOLOGIN_USER_HINT, + PROP_AUTOLOGIN_GUEST_HINT, + PROP_AUTOLOGIN_TIMEOUT_HINT, + PROP_AUTHENTICATION_USER, + PROP_IN_AUTHENTICATION, + PROP_IS_AUTHENTICATED, +}; + +enum { + SHOW_PROMPT, + SHOW_MESSAGE, + AUTHENTICATION_COMPLETE, + AUTOLOGIN_TIMER_EXPIRED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct +{ + gboolean connected; + + GIOChannel *to_server_channel, *from_server_channel; + guint8 *read_buffer; + gsize n_read; + + gsize n_responses_waiting; + GList *responses_received; + + GHashTable *hints; + guint autologin_timeout; + + gchar *authentication_user; + gboolean in_authentication; + gboolean is_authenticated; + guint32 authenticate_sequence_number; + gboolean cancelling_authentication; +} LightDMGreeterPrivate; + +G_DEFINE_TYPE (LightDMGreeter, lightdm_greeter, G_TYPE_OBJECT); + +#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_GREETER, LightDMGreeterPrivate) + +#define HEADER_SIZE 8 +#define MAX_MESSAGE_LENGTH 1024 + +/* Messages from the greeter to the server */ +typedef enum +{ + GREETER_MESSAGE_CONNECT = 0, + GREETER_MESSAGE_AUTHENTICATE, + GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, + GREETER_MESSAGE_CONTINUE_AUTHENTICATION, + GREETER_MESSAGE_START_SESSION, + GREETER_MESSAGE_CANCEL_AUTHENTICATION, + GREETER_MESSAGE_SET_LANGUAGE, + GREETER_MESSAGE_AUTHENTICATE_REMOTE +} GreeterMessage; + +/* Messages from the server to the greeter */ +typedef enum +{ + SERVER_MESSAGE_CONNECTED = 0, + SERVER_MESSAGE_PROMPT_AUTHENTICATION, + SERVER_MESSAGE_END_AUTHENTICATION, + SERVER_MESSAGE_SESSION_RESULT +} ServerMessage; + +/** + * lightdm_greeter_new: + * + * Create a new greeter. + * + * Return value: the new #LightDMGreeter + **/ +LightDMGreeter * +lightdm_greeter_new () +{ + return g_object_new (LIGHTDM_TYPE_GREETER, NULL); +} + +static gboolean +timed_login_cb (gpointer data) +{ + LightDMGreeter *greeter = data; + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + + priv->autologin_timeout = 0; + g_signal_emit (G_OBJECT (greeter), signals[AUTOLOGIN_TIMER_EXPIRED], 0); + + return FALSE; +} + +static guint32 +int_length () +{ + return 4; +} + +static void +write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset) +{ + if (*offset + 4 >= buffer_length) + return; + buffer[*offset] = value >> 24; + buffer[*offset+1] = (value >> 16) & 0xFF; + buffer[*offset+2] = (value >> 8) & 0xFF; + buffer[*offset+3] = value & 0xFF; + *offset += 4; +} + +static void +write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset) +{ + gint length = 0; + + if (value) + length = strlen (value); + write_int (buffer, buffer_length, length, offset); + if (*offset + length >= buffer_length) + return; + memcpy (buffer + *offset, value, length); + *offset += length; +} + +static guint32 +read_int (guint8 *message, gsize message_length, gsize *offset) +{ + guint32 value; + guint8 *buffer; + + if (message_length - *offset < int_length ()) + { + g_warning ("Not enough space for int, need %i, got %zi", int_length (), message_length - *offset); + return 0; + } + + buffer = message + *offset; + value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; + *offset += int_length (); + + return value; +} + +static gchar * +read_string (guint8 *message, gsize message_length, gsize *offset) +{ + guint32 length; + gchar *value; + + length = read_int (message, message_length, offset); + if (message_length - *offset < length) + { + g_warning ("Not enough space for string, need %u, got %zu", length, message_length - *offset); + return g_strdup (""); + } + + value = g_malloc (sizeof (gchar) * (length + 1)); + memcpy (value, message + *offset, length); + value[length] = '\0'; + *offset += length; + + return value; +} + +static guint32 +string_length (const gchar *value) +{ + if (value) + return int_length () + strlen (value); + else + return int_length (); +} + +static void +write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset) +{ + write_int (buffer, buffer_length, id, offset); + write_int (buffer, buffer_length, length, offset); +} + +static guint32 +get_message_length (guint8 *message, gsize message_length) +{ + gsize offset = 4; + return read_int (message, message_length, &offset); +} + +static void +write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + GIOStatus status; + GError *error = NULL; + guint32 stated_length; + + /* Double check that we're sending well-formed messages. If we say we're + sending more than we do, we end up DOS'ing lightdm as it waits for the + rest. If we say we're sending less than we do, we confuse the heck out + of lightdm, as it starts reading headers from the middle of our + messages. */ + stated_length = HEADER_SIZE + get_message_length (message, message_length); + if (stated_length != message_length) + { + g_warning ("Refusing to write malformed packet to daemon: declared size is %u, but actual size is %zu", stated_length, message_length); + return; + } + + status = g_io_channel_write_chars (priv->to_server_channel, (gchar *) message, message_length, NULL, &error); + if (error) + g_warning ("Error writing to daemon: %s", error->message); + g_clear_error (&error); + if (status == G_IO_STATUS_NORMAL) + g_debug ("Wrote %zi bytes to daemon", message_length); + g_io_channel_flush (priv->to_server_channel, NULL); +} + +static void +handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + gchar *version; + GString *hint_string; + int timeout; + + version = read_string (message, message_length, offset); + hint_string = g_string_new (""); + while (*offset < message_length) + { + gchar *name, *value; + + name = read_string (message, message_length, offset); + value = read_string (message, message_length, offset); + g_hash_table_insert (priv->hints, name, value); + g_string_append_printf (hint_string, " %s=%s", name, value); + } + + g_debug ("Connected version=%s%s", version, hint_string->str); + g_free (version); + g_string_free (hint_string, TRUE); + + /* Set timeout for default login */ + timeout = lightdm_greeter_get_autologin_timeout_hint (greeter); + if (timeout) + { + g_debug ("Setting autologin timer for %d seconds", timeout); + priv->autologin_timeout = g_timeout_add (timeout * 1000, timed_login_cb, greeter); + } +} + +static void +handle_prompt_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + guint32 sequence_number, n_messages, i; + gchar *username; + + sequence_number = read_int (message, message_length, offset); + if (sequence_number != priv->authenticate_sequence_number) + { + g_debug ("Ignoring prompt authentication with invalid sequence number %d", sequence_number); + return; + } + + if (priv->cancelling_authentication) + { + g_debug ("Ignoring prompt authentication as waiting for it to cancel"); + return; + } + + /* Update username */ + username = read_string (message, message_length, offset); + if (strcmp (username, "") == 0) + { + g_free (username); + username = NULL; + } + g_free (priv->authentication_user); + priv->authentication_user = username; + + g_list_free_full (priv->responses_received, g_free); + priv->responses_received = NULL; + priv->n_responses_waiting = 0; + + n_messages = read_int (message, message_length, offset); + g_debug ("Prompt user with %d message(s)", n_messages); + + for (i = 0; i < n_messages; i++) + { + int style; + gchar *text; + + style = read_int (message, message_length, offset); + text = read_string (message, message_length, offset); + + // FIXME: Should stop on prompts? + switch (style) + { + case PAM_PROMPT_ECHO_OFF: + priv->n_responses_waiting++; + g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_SECRET); + break; + case PAM_PROMPT_ECHO_ON: + priv->n_responses_waiting++; + g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_QUESTION); + break; + case PAM_ERROR_MSG: + g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_ERROR); + break; + case PAM_TEXT_INFO: + g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_INFO); + break; + } + + g_free (text); + } +} + +static void +handle_end_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + guint32 sequence_number, return_code; + gchar *username; + + sequence_number = read_int (message, message_length, offset); + + if (sequence_number != priv->authenticate_sequence_number) + { + g_debug ("Ignoring end authentication with invalid sequence number %d", sequence_number); + return; + } + + username = read_string (message, message_length, offset); + return_code = read_int (message, message_length, offset); + + g_debug ("Authentication complete for user %s with return code %d", username, return_code); + + /* Update username */ + if (strcmp (username, "") == 0) + { + g_free (username); + username = NULL; + } + g_free (priv->authentication_user); + priv->authentication_user = username; + + priv->cancelling_authentication = FALSE; + priv->is_authenticated = (return_code == 0); + + priv->in_authentication = FALSE; + g_signal_emit (G_OBJECT (greeter), signals[AUTHENTICATION_COMPLETE], 0); +} + +static guint8 * +read_message (LightDMGreeter *greeter, gsize *length, gboolean block) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + gsize n_to_read, n_read; + guint8 *buffer; + GError *error = NULL; + + /* Read the header, or the whole message if we already have that */ + n_to_read = HEADER_SIZE; + if (priv->n_read >= HEADER_SIZE) + n_to_read += get_message_length (priv->read_buffer, priv->n_read); + + do + { + GIOStatus status; + status = g_io_channel_read_chars (priv->from_server_channel, + (gchar *) priv->read_buffer + priv->n_read, + n_to_read - priv->n_read, + &n_read, + &error); + if (error) + g_warning ("Error reading from server: %s", error->message); + g_clear_error (&error); + if (status != G_IO_STATUS_NORMAL) + break; + + g_debug ("Read %zi bytes from daemon", n_read); + + priv->n_read += n_read; + } while (priv->n_read < n_to_read && block); + + /* Stop if haven't got all the data we want */ + if (priv->n_read != n_to_read) + return FALSE; + + /* If have header, rerun for content */ + if (priv->n_read == HEADER_SIZE) + { + n_to_read = get_message_length (priv->read_buffer, priv->n_read); + if (n_to_read > 0) + { + priv->read_buffer = g_realloc (priv->read_buffer, HEADER_SIZE + n_to_read); + return read_message (greeter, length, block); + } + } + + buffer = priv->read_buffer; + *length = priv->n_read; + + priv->read_buffer = g_malloc (priv->n_read); + priv->n_read = 0; + + return buffer; +} + +static gboolean +from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data) +{ + LightDMGreeter *greeter = data; + guint8 *message; + gsize message_length, offset; + guint32 id; + + message = read_message (greeter, &message_length, FALSE); + if (!message) + return TRUE; + + offset = 0; + id = read_int (message, message_length, &offset); + read_int (message, message_length, &offset); + switch (id) + { + case SERVER_MESSAGE_PROMPT_AUTHENTICATION: + handle_prompt_authentication (greeter, message, message_length, &offset); + break; + case SERVER_MESSAGE_END_AUTHENTICATION: + handle_end_authentication (greeter, message, message_length, &offset); + break; + default: + g_warning ("Unknown message from server: %d", id); + break; + } + g_free (message); + + return TRUE; +} + +/** + * lightdm_greeter_connect_sync: + * @greeter: The greeter to connect + * @error: return location for a #GError, or %NULL + * + * Connects the greeter to the display manager. Will block until connected. + * + * Return value: #TRUE if successfully connected + **/ +gboolean +lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error) +{ + LightDMGreeterPrivate *priv; + const gchar *fd; + guint8 message[MAX_MESSAGE_LENGTH]; + guint8 *response; + gsize response_length, offset = 0; + guint32 id; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + + priv = GET_PRIVATE (greeter); + + fd = g_getenv ("LIGHTDM_TO_SERVER_FD"); + if (!fd) + { + g_warning ("No LIGHTDM_TO_SERVER_FD environment variable"); + return FALSE; + } + priv->to_server_channel = g_io_channel_unix_new (atoi (fd)); + g_io_channel_set_encoding (priv->to_server_channel, NULL, NULL); + + fd = g_getenv ("LIGHTDM_FROM_SERVER_FD"); + if (!fd) + { + g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable"); + return FALSE; + } + priv->from_server_channel = g_io_channel_unix_new (atoi (fd)); + g_io_channel_set_encoding (priv->from_server_channel, NULL, NULL); + g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter); + + g_debug ("Connecting to display manager..."); + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION), &offset); + write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset); + write_message (greeter, message, offset); + + response = read_message (greeter, &response_length, TRUE); + if (!response) + return FALSE; + + offset = 0; + id = read_int (response, response_length, &offset); + read_int (response, response_length, &offset); + if (id == SERVER_MESSAGE_CONNECTED) + handle_connected (greeter, response, response_length, &offset); + g_free (response); + if (id != SERVER_MESSAGE_CONNECTED) + { + g_warning ("Expected CONNECTED message, got %d", id); + return FALSE; + } + + priv->connected = TRUE; + + return TRUE; +} + +/** + * lightdm_greeter_get_hint: + * @greeter: A #LightDMGreeter + * @name: The hint name to query. + * + * Get a hint. + * + * Return value: The value for this hint or #NULL if not set. + **/ +const gchar * +lightdm_greeter_get_hint (LightDMGreeter *greeter, const gchar *name) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL); + return g_hash_table_lookup (GET_PRIVATE (greeter)->hints, name); +} + +/** + * lightdm_greeter_get_default_session_hint: + * @greeter: A #LightDMGreeter + * + * Get the default session to use. + * + * Return value: The session name + **/ +const gchar * +lightdm_greeter_get_default_session_hint (LightDMGreeter *greeter) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL); + return lightdm_greeter_get_hint (greeter, "default-session"); +} + +/** + * lightdm_greeter_get_hide_users_hint: + * @greeter: A #LightDMGreeter + * + * Check if user accounts should be shown. If this is TRUE then the list of + * accounts should be taken from #LightDMUserList and displayed in the greeter + * for the user to choose from. Note that this list can be empty and it is + * recommended you show a method for the user to enter a username manually. + * + * If this option is shown the greeter should only allow these users to be + * chosen for login unless the manual login hint is set. + * + * Return value: #TRUE if the available users should not be shown. + */ +gboolean +lightdm_greeter_get_hide_users_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "hide-users"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_show_manual_login_hint: + * @greeter: A #LightDMGreeter + * + * Check if a manual login option should be shown. If set the GUI + * should provide a way for a username to be entered manually. + * Without this hint a greeter which is showing a user list can + * limit logins to only those users. + * + * Return value: #TRUE if a manual login option should be shown. + */ +gboolean +lightdm_greeter_get_show_manual_login_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "show-manual-login"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_show_remote_login_hint: + * @greeter: A #LightDMGreeter + * + * Check if a remote login option should be shown. If set the GUI + * should provide a way for a user to log into a remote desktop server. + * + * Return value: #TRUE if a remote login option should be shown. + */ +gboolean +lightdm_greeter_get_show_remote_login_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "show-remote-login"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_lock_hint: + * @greeter: A #LightDMGreeter + * + * Check if the greeter is acting as a lock screen. + * + * Return value: #TRUE if the greeter was triggered by locking the seat. + */ +gboolean +lightdm_greeter_get_lock_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "lock-screen"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_has_guest_account_hint: + * @greeter: A #LightDMGreeter + * + * Check if guest sessions are supported. + * + * Return value: #TRUE if guest sessions are supported. + */ +gboolean +lightdm_greeter_get_has_guest_account_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "has-guest-account"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_select_user_hint: + * @greeter: A #LightDMGreeter + * + * Get the user to select by default. + * + * Return value: A username + */ +const gchar * +lightdm_greeter_get_select_user_hint (LightDMGreeter *greeter) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL); + return lightdm_greeter_get_hint (greeter, "select-user"); +} + +/** + * lightdm_greeter_get_select_guest_hint: + * @greeter: A #LightDMGreeter + * + * Check if the guest account should be selected by default. + * + * Return value: #TRUE if the guest account should be selected by default. + */ +gboolean +lightdm_greeter_get_select_guest_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "select-guest"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_autologin_user_hint: + * @greeter: A #LightDMGreeter + * + * Get the user account to automatically logg into when the timer expires. + * + * Return value: The user account to automatically log into. + */ +const gchar * +lightdm_greeter_get_autologin_user_hint (LightDMGreeter *greeter) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL); + return lightdm_greeter_get_hint (greeter, "autologin-user"); +} + +/** + * lightdm_greeter_get_autologin_guest_hint: + * @greeter: A #LightDMGreeter + * + * Check if the guest account should be automatically logged into when the timer expires. + * + * Return value: #TRUE if the guest account should be automatically logged into. + */ +gboolean +lightdm_greeter_get_autologin_guest_hint (LightDMGreeter *greeter) +{ + const gchar *value; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "autologin-guest"); + + return g_strcmp0 (value, "true") == 0; +} + +/** + * lightdm_greeter_get_autologin_timeout_hint: + * @greeter: A #LightDMGreeter + * + * Get the number of seconds to wait before automaitcally logging in. + * + * Return value: The number of seconds to wait before automatically logging in or 0 for no timeout. + */ +gint +lightdm_greeter_get_autologin_timeout_hint (LightDMGreeter *greeter) +{ + const gchar *value; + gint timeout = 0; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + value = lightdm_greeter_get_hint (greeter, "autologin-timeout"); + if (value) + timeout = atoi (value); + if (timeout < 0) + timeout = 0; + + return timeout; +} + +/** + * lightdm_greeter_cancel_autologin: + * @greeter: A #LightDMGreeter + * + * Cancel the automatic login. + */ +void +lightdm_greeter_cancel_autologin (LightDMGreeter *greeter) +{ + LightDMGreeterPrivate *priv; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + + priv = GET_PRIVATE (greeter); + + if (priv->autologin_timeout) + g_source_remove (priv->autologin_timeout); + priv->autologin_timeout = 0; +} + +/** + * lightdm_greeter_authenticate: + * @greeter: A #LightDMGreeter + * @username: (allow-none): A username or #NULL to prompt for a username. + * + * Starts the authentication procedure for a user. + **/ +void +lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + gsize offset = 0; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + + priv = GET_PRIVATE (greeter); + + g_return_if_fail (priv->connected); + + priv->cancelling_authentication = FALSE; + priv->authenticate_sequence_number++; + priv->in_authentication = TRUE; + priv->is_authenticated = FALSE; + if (username != priv->authentication_user) + { + g_free (priv->authentication_user); + priv->authentication_user = g_strdup (username); + } + + g_debug ("Starting authentication for user %s...", username); + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE, int_length () + string_length (username), &offset); + write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset); + write_string (message, MAX_MESSAGE_LENGTH, username, &offset); + write_message (greeter, message, offset); +} + +/** + * lightdm_greeter_authenticate_as_guest: + * @greeter: A #LightDMGreeter + * + * Starts the authentication procedure for the guest user. + **/ +void +lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + gsize offset = 0; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + + priv = GET_PRIVATE (greeter); + + g_return_if_fail (priv->connected); + + priv->cancelling_authentication = FALSE; + priv->authenticate_sequence_number++; + priv->in_authentication = TRUE; + priv->is_authenticated = FALSE; + g_free (priv->authentication_user); + priv->authentication_user = NULL; + + g_debug ("Starting authentication for guest account..."); + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, int_length (), &offset); + write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset); + write_message (greeter, message, offset); +} + +/** + * lightdm_greeter_authenticate_autologin: + * @greeter: A #LightDMGreeter + * + * Starts the authentication procedure for the automatic login user. + **/ +void +lightdm_greeter_authenticate_autologin (LightDMGreeter *greeter) +{ + const gchar *user; + + user = lightdm_greeter_get_autologin_user_hint (greeter); + if (lightdm_greeter_get_autologin_guest_hint (greeter)) + lightdm_greeter_authenticate_as_guest (greeter); + else if (user) + lightdm_greeter_authenticate (greeter, user); +} + +/** + * lightdm_greeter_authenticate_remote: + * @greeter: A #LightDMGreeter + * @session: The name of a remote session + * @username: (allow-none): A username of #NULL to prompt for a username. + * + * Start authentication for a remote session type. + **/ +void +lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *session, const gchar *username) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + gsize offset = 0; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + + priv = GET_PRIVATE (greeter); + + g_return_if_fail (priv->connected); + + priv->cancelling_authentication = FALSE; + priv->authenticate_sequence_number++; + priv->in_authentication = TRUE; + priv->is_authenticated = FALSE; + g_free (priv->authentication_user); + priv->authentication_user = NULL; + + if (username) + g_debug ("Starting authentication for remote session %s as user %s...", session, username); + else + g_debug ("Starting authentication for remote session %s...", session); + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_REMOTE, int_length () + string_length (session) + string_length (username), &offset); + write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset); + write_string (message, MAX_MESSAGE_LENGTH, session, &offset); + write_string (message, MAX_MESSAGE_LENGTH, username, &offset); + write_message (greeter, message, offset); +} + +/** + * lightdm_greeter_respond: + * @greeter: A #LightDMGreeter + * @response: Response to a prompt + * + * Provide response to a prompt. May be one in a series. + **/ +void +lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + gsize offset = 0; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + g_return_if_fail (response != NULL); + + priv = GET_PRIVATE (greeter); + + g_return_if_fail (priv->connected); + g_return_if_fail (priv->n_responses_waiting > 0); + + priv->n_responses_waiting--; + priv->responses_received = g_list_append (priv->responses_received, g_strdup (response)); + + if (priv->n_responses_waiting == 0) + { + guint32 msg_length; + GList *iter; + + g_debug ("Providing response to display manager"); + + msg_length = int_length (); + for (iter = priv->responses_received; iter; iter = iter->next) + { + msg_length += string_length ((gchar *)iter->data); + } + + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset); + write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset); + for (iter = priv->responses_received; iter; iter = iter->next) + { + write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset); + } + write_message (greeter, message, offset); + + g_list_free_full (priv->responses_received, g_free); + priv->responses_received = NULL; + } +} + +/** + * lightdm_greeter_cancel_authentication: + * @greeter: A #LightDMGreeter + * + * Cancel the current user authentication. + **/ +void +lightdm_greeter_cancel_authentication (LightDMGreeter *greeter) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + gsize offset = 0; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + + priv = GET_PRIVATE (greeter); + + g_return_if_fail (priv->connected); + + priv->cancelling_authentication = TRUE; + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset); + write_message (greeter, message, offset); +} + +/** + * lightdm_greeter_get_in_authentication: + * @greeter: A #LightDMGreeter + * + * Checks if the greeter is in the process of authenticating. + * + * Return value: #TRUE if the greeter is authenticating a user. + **/ +gboolean +lightdm_greeter_get_in_authentication (LightDMGreeter *greeter) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + return GET_PRIVATE (greeter)->in_authentication; +} + +/** + * lightdm_greeter_get_is_authenticated: + * @greeter: A #LightDMGreeter + * + * Checks if the greeter has successfully authenticated. + * + * Return value: #TRUE if the greeter is authenticated for login. + **/ +gboolean +lightdm_greeter_get_is_authenticated (LightDMGreeter *greeter) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + return GET_PRIVATE (greeter)->is_authenticated; +} + +/** + * lightdm_greeter_get_authentication_user: + * @greeter: A #LightDMGreeter + * + * Get the user that is being authenticated. + * + * Return value: The username of the authentication user being authenticated or #NULL if no authentication in progress. + */ +const gchar * +lightdm_greeter_get_authentication_user (LightDMGreeter *greeter) +{ + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL); + return GET_PRIVATE (greeter)->authentication_user; +} + +/** + * lightdm_greeter_set_language: + * @greeter: A #LightDMGreeter + * @language: The language to use for this user. + * + * Set the language for the currently authenticated user. + **/ +void +lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + gsize offset = 0; + + g_return_if_fail (LIGHTDM_IS_GREETER (greeter)); + + priv = GET_PRIVATE (greeter); + + g_return_if_fail (priv->connected); + + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SET_LANGUAGE, string_length (language), &offset); + write_string (message, MAX_MESSAGE_LENGTH, language, &offset); + write_message (greeter, message, offset); +} + +/** + * lightdm_greeter_start_session_sync: + * @greeter: A #LightDMGreeter + * @session: (allow-none): The session to log into or #NULL to use the default. + * @error: return location for a #GError, or %NULL + * + * Start a session for the authenticated user. + * + * Return value: TRUE if the session was started. + **/ +gboolean +lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *session, GError **error) +{ + LightDMGreeterPrivate *priv; + guint8 message[MAX_MESSAGE_LENGTH]; + guint8 *response; + gsize response_length, offset = 0; + guint32 id, return_code = 1; + + g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE); + + priv = GET_PRIVATE (greeter); + + g_return_val_if_fail (priv->connected, FALSE); + g_return_val_if_fail (priv->is_authenticated, FALSE); + + if (session) + g_debug ("Starting session %s", session); + else + g_debug ("Starting default session"); + + write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset); + write_string (message, MAX_MESSAGE_LENGTH, session, &offset); + write_message (greeter, message, offset); + + response = read_message (greeter, &response_length, TRUE); + if (!response) + return FALSE; + + offset = 0; + id = read_int (response, response_length, &offset); + read_int (response, response_length, &offset); + if (id == SERVER_MESSAGE_SESSION_RESULT) + return_code = read_int (response, response_length, &offset); + else + g_warning ("Expected SESSION_RESULT message, got %d", id); + + g_free (response); + + return return_code == 0; +} + +static void +lightdm_greeter_init (LightDMGreeter *greeter) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + + priv->read_buffer = g_malloc (HEADER_SIZE); + priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +} + +static void +lightdm_greeter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +lightdm_greeter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + LightDMGreeter *self; + + self = LIGHTDM_GREETER (object); + + switch (prop_id) { + case PROP_DEFAULT_SESSION_HINT: + g_value_set_string (value, lightdm_greeter_get_default_session_hint (self)); + break; + case PROP_HIDE_USERS_HINT: + g_value_set_boolean (value, lightdm_greeter_get_hide_users_hint (self)); + break; + case PROP_SHOW_MANUAL_LOGIN_HINT: + g_value_set_boolean (value, lightdm_greeter_get_show_manual_login_hint (self)); + break; + case PROP_SHOW_REMOTE_LOGIN_HINT: + g_value_set_boolean (value, lightdm_greeter_get_show_remote_login_hint (self)); + break; + case PROP_LOCK_HINT: + g_value_set_boolean (value, lightdm_greeter_get_lock_hint (self)); + break; + case PROP_HAS_GUEST_ACCOUNT_HINT: + g_value_set_boolean (value, lightdm_greeter_get_has_guest_account_hint (self)); + break; + case PROP_SELECT_USER_HINT: + g_value_set_string (value, lightdm_greeter_get_select_user_hint (self)); + break; + case PROP_SELECT_GUEST_HINT: + g_value_set_boolean (value, lightdm_greeter_get_select_guest_hint (self)); + break; + case PROP_AUTOLOGIN_USER_HINT: + g_value_set_string (value, lightdm_greeter_get_autologin_user_hint (self)); + break; + case PROP_AUTOLOGIN_GUEST_HINT: + g_value_set_boolean (value, lightdm_greeter_get_autologin_guest_hint (self)); + break; + case PROP_AUTOLOGIN_TIMEOUT_HINT: + g_value_set_int (value, lightdm_greeter_get_autologin_timeout_hint (self)); + break; + case PROP_AUTHENTICATION_USER: + g_value_set_string (value, lightdm_greeter_get_authentication_user (self)); + break; + case PROP_IN_AUTHENTICATION: + g_value_set_boolean (value, lightdm_greeter_get_in_authentication (self)); + break; + case PROP_IS_AUTHENTICATED: + g_value_set_boolean (value, lightdm_greeter_get_is_authenticated (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +marshal_VOID__STRING_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1, + gpointer arg_1, + gint arg_2, + gpointer data2); + register GMarshalFunc_VOID__STRING_INT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + (param_values + 1)->data[0].v_pointer, + (param_values + 2)->data[0].v_int, + data2); +} + +static void +lightdm_greeter_finalize (GObject *object) +{ + LightDMGreeter *self = LIGHTDM_GREETER (object); + LightDMGreeterPrivate *priv = GET_PRIVATE (self); + + if (priv->to_server_channel) + g_io_channel_unref (priv->to_server_channel); + if (priv->from_server_channel) + g_io_channel_unref (priv->from_server_channel); + g_free (priv->authentication_user); + g_hash_table_unref (priv->hints); + + G_OBJECT_CLASS (lightdm_greeter_parent_class)->finalize (object); +} + +static void +lightdm_greeter_class_init (LightDMGreeterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LightDMGreeterPrivate)); + + object_class->set_property = lightdm_greeter_set_property; + object_class->get_property = lightdm_greeter_get_property; + object_class->finalize = lightdm_greeter_finalize; + + g_object_class_install_property (object_class, + PROP_DEFAULT_SESSION_HINT, + g_param_spec_string ("default-session-hint", + "default-session-hint", + "Default session hint", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_HIDE_USERS_HINT, + g_param_spec_boolean ("hide-users-hint", + "hide-users-hint", + "Hide users hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_SHOW_MANUAL_LOGIN_HINT, + g_param_spec_boolean ("show-manual-login-hint", + "show-manual-login-hint", + "Show manual login hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_SHOW_REMOTE_LOGIN_HINT, + g_param_spec_boolean ("show-remote-login-hint", + "show-remote-login-hint", + "Show remote login hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_LOCK_HINT, + g_param_spec_boolean ("lock-hint", + "lock-hint", + "Lock hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_HAS_GUEST_ACCOUNT_HINT, + g_param_spec_boolean ("has-guest-account-hint", + "has-guest-account-hint", + "Has guest account hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_SELECT_USER_HINT, + g_param_spec_string ("select-user-hint", + "select-user-hint", + "Select user hint", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_SELECT_GUEST_HINT, + g_param_spec_boolean ("select-guest-hint", + "select-guest-hint", + "Select guest account hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_AUTOLOGIN_USER_HINT, + g_param_spec_string ("autologin-user-hint", + "autologin-user-hint", + "Autologin user hint", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_AUTOLOGIN_GUEST_HINT, + g_param_spec_boolean ("autologin-guest-hint", + "autologin-guest-hint", + "Autologin guest account hint", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_AUTOLOGIN_TIMEOUT_HINT, + g_param_spec_int ("autologin-timeout-hint", + "autologin-timeout-hint", + "Autologin timeout hint", + 0, G_MAXINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_AUTHENTICATION_USER, + g_param_spec_string ("authentication-user", + "authentication-user", + "The user being authenticated", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_IN_AUTHENTICATION, + g_param_spec_boolean ("in-authentication", + "in-authentication", + "TRUE if a user is being authenticated", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_IS_AUTHENTICATED, + g_param_spec_boolean ("is-authenticated", + "is-authenticated", + "TRUE if the selected user is authenticated", + FALSE, + G_PARAM_READABLE)); + + /** + * LightDMGreeter::show-prompt: + * @greeter: A #LightDMGreeter + * @text: Prompt text + * @type: Prompt type + * + * The ::show-prompt signal gets emitted when the greeter should show a + * prompt to the user. The given text should be displayed and an input + * field for the user to provide a response. + * + * Call lightdm_greeter_respond() with the resultant input or + * lightdm_greeter_cancel_authentication() to abort the authentication. + **/ + signals[SHOW_PROMPT] = + g_signal_new ("show-prompt", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMGreeterClass, show_prompt), + NULL, NULL, + marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); + + /** + * LightDMGreeter::show-message: + * @greeter: A #LightDMGreeter + * @text: Message text + * @type: Message type + * + * The ::show-message signal gets emitted when the greeter + * should show a message to the user. + **/ + signals[SHOW_MESSAGE] = + g_signal_new ("show-message", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMGreeterClass, show_message), + NULL, NULL, + marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); + + /** + * LightDMGreeter::authentication-complete: + * @greeter: A #LightDMGreeter + * + * The ::authentication-complete signal gets emitted when the greeter + * has completed authentication. + * + * Call lightdm_greeter_get_is_authenticated() to check if the authentication + * was successful. + **/ + signals[AUTHENTICATION_COMPLETE] = + g_signal_new ("authentication-complete", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMGreeterClass, authentication_complete), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * LightDMGreeter::autologin-timer-expired: + * @greeter: A #LightDMGreeter + * + * The ::timed-login signal gets emitted when the automatic login timer has expired. + * The application should then call lightdm_greeter_login(). + **/ + signals[AUTOLOGIN_TIMER_EXPIRED] = + g_signal_new ("autologin-timer-expired", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMGreeterClass, autologin_timer_expired), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c new file mode 100644 index 00000000..9709619e --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2010 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include +#include +#include +#include +#include + +#include "lightdm/language.h" + +enum { + PROP_0, + PROP_CODE, + PROP_NAME, + PROP_TERRITORY +}; + +typedef struct +{ + gchar *code; + gchar *name; + gchar *territory; +} LightDMLanguagePrivate; + +G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT); + +#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate) + +static gboolean have_languages = FALSE; +static GList *languages = NULL; + +static void +update_languages (void) +{ + gchar *command = "locale -a"; + gchar *stdout_text = NULL, *stderr_text = NULL; + gint exit_status; + gboolean result; + GError *error = NULL; + + if (have_languages) + return; + + result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error); + if (error) + { + g_warning ("Failed to run '%s': %s", command, error->message); + g_clear_error (&error); + } + else if (exit_status != 0) + g_warning ("Failed to get languages, '%s' returned %d", command, exit_status); + else if (result) + { + gchar **tokens; + int i; + + tokens = g_strsplit_set (stdout_text, "\n\r", -1); + for (i = 0; tokens[i]; i++) + { + LightDMLanguage *language; + gchar *code; + + code = g_strchug (tokens[i]); + if (code[0] == '\0') + continue; + + /* Ignore the non-interesting languages */ + if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8")) + continue; + + language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL); + languages = g_list_append (languages, language); + } + + g_strfreev (tokens); + } + + g_free (stdout_text); + g_free (stderr_text); + + have_languages = TRUE; +} + +static gboolean +is_utf8 (const gchar *code) +{ + return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8"); +} + +/* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */ +static gchar * +get_locale_name (const gchar *code) +{ + gchar *locale = NULL, *language; + char *at; + static gchar **avail_locales; + gint i; + + if (is_utf8 (code)) + return (gchar *) code; + + if ((at = strchr (code, '@'))) + language = g_strndup (code, at - code); + else + language = g_strdup (code); + + if (!avail_locales) + { + gchar *locales; + GError *error = NULL; + + if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error)) + { + avail_locales = g_strsplit (g_strchomp (locales), "\n", -1); + g_free (locales); + } + else + { + g_warning ("Failed to run 'locale -a': %s", error->message); + g_clear_error (&error); + } + } + + if (avail_locales) + { + for (i = 0; avail_locales[i]; i++) + { + gchar *loc = avail_locales[i]; + if (!g_strrstr (loc, ".utf8")) + continue; + if (g_str_has_prefix (loc, language)) + { + locale = g_strdup (loc); + break; + } + } + } + + g_free (language); + + return locale; +} + +/** + * lightdm_get_language: + * + * Get the current language. + * + * Return value: (transfer none): The current language or #NULL if no language. + **/ +LightDMLanguage * +lightdm_get_language (void) +{ + const gchar *lang; + GList *link; + + lang = g_getenv ("LANG"); + if (!lang) + return NULL; + + for (link = lightdm_get_languages (); link; link = link->next) + { + LightDMLanguage *language = link->data; + if (lightdm_language_matches (language, lang)) + return language; + } + + return NULL; +} + +/** + * lightdm_get_languages: + * + * Get a list of languages to present to the user. + * + * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user. + **/ +GList * +lightdm_get_languages (void) +{ + update_languages (); + return languages; +} + +/** + * lightdm_language_get_code: + * @language: A #LightDMLanguage + * + * Get the code of a language. + * + * Return value: The code of the language + **/ +const gchar * +lightdm_language_get_code (LightDMLanguage *language) +{ + g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL); + return GET_PRIVATE (language)->code; +} + +/** + * lightdm_language_get_name: + * @language: A #LightDMLanguage + * + * Get the name of a language. + * + * Return value: The name of the language + **/ +const gchar * +lightdm_language_get_name (LightDMLanguage *language) +{ + LightDMLanguagePrivate *priv; + + g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL); + + priv = GET_PRIVATE (language); + + if (!priv->name) + { + gchar *locale = get_locale_name (priv->code); + if (locale) + { + gchar *current = setlocale (LC_ALL, NULL); + setlocale (LC_IDENTIFICATION, locale); + setlocale (LC_MESSAGES, ""); + + gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE); + if (language_en && strlen (language_en) > 0) + priv->name = g_strdup (dgettext ("iso_639_3", language_en)); + + setlocale (LC_ALL, current); + } + if (!priv->name) + { + gchar **tokens = g_strsplit_set (priv->code, "_.@", 2); + priv->name = g_strdup (tokens[0]); + g_strfreev (tokens); + } + } + + return priv->name; +} + +/** + * lightdm_language_get_territory: + * @language: A #LightDMLanguage + * + * Get the territory the language is used in. + * + * Return value: The territory the language is used in. + **/ +const gchar * +lightdm_language_get_territory (LightDMLanguage *language) +{ + LightDMLanguagePrivate *priv; + + g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL); + + priv = GET_PRIVATE (language); + + if (!priv->territory && strchr (priv->code, '_')) + { + gchar *locale = get_locale_name (priv->code); + if (locale) + { + gchar *current = setlocale (LC_ALL, NULL); + setlocale (LC_IDENTIFICATION, locale); + setlocale (LC_MESSAGES, ""); + + gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY); + if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0) + priv->territory = g_strdup (dgettext ("iso_3166", country_en)); + + setlocale (LC_ALL, current); + } + if (!priv->territory) + { + gchar **tokens = g_strsplit_set (priv->code, "_.@", 3); + priv->territory = g_strdup (tokens[1]); + g_strfreev (tokens); + } + } + + return priv->territory; +} + +/** + * lightdm_language_matches: + * @language: A #LightDMLanguage + * @code: A language code + * + * Check if a language code matches this language. + * + * Return value: #TRUE if the code matches this language. + **/ +gboolean +lightdm_language_matches (LightDMLanguage *language, const gchar *code) +{ + LightDMLanguagePrivate *priv; + + g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE); + g_return_val_if_fail (code != NULL, FALSE); + + priv = GET_PRIVATE (language); + + /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */ + if (is_utf8 (priv->code) && is_utf8 (code)) + { + /* Match the characters before the '.' */ + int i; + for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++); + return priv->code[i] == '.' && code[i] == '.'; + } + + return g_str_equal (priv->code, code); +} + +static void +lightdm_language_init (LightDMLanguage *language) +{ +} + +static void +lightdm_language_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + LightDMLanguage *self = LIGHTDM_LANGUAGE (object); + LightDMLanguagePrivate *priv = GET_PRIVATE (self); + + switch (prop_id) { + case PROP_CODE: + g_free (priv->name); + priv->code = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_language_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + LightDMLanguage *self; + + self = LIGHTDM_LANGUAGE (object); + + switch (prop_id) { + case PROP_CODE: + g_value_set_string (value, lightdm_language_get_code (self)); + break; + case PROP_NAME: + g_value_set_string (value, lightdm_language_get_name (self)); + break; + case PROP_TERRITORY: + g_value_set_string (value, lightdm_language_get_territory (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_language_class_init (LightDMLanguageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate)); + + object_class->set_property = lightdm_language_set_property; + object_class->get_property = lightdm_language_get_property; + + g_object_class_install_property (object_class, + PROP_CODE, + g_param_spec_string ("code", + "code", + "Language code", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "Name of the language", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_TERRITORY, + g_param_spec_string ("territory", + "territory", + "Territory the language is from", + NULL, + G_PARAM_READABLE)); +} diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c new file mode 100644 index 00000000..d5057450 --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c @@ -0,0 +1,344 @@ +/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*- + * + * Copyright (C) 2010 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include + +#include "lightdm/layout.h" + +enum { + PROP_0, + PROP_NAME, + PROP_SHORT_DESCRIPTION, + PROP_DESCRIPTION +}; + +typedef struct +{ + gchar *name; + gchar *short_description; + gchar *description; +} LightDMLayoutPrivate; + +G_DEFINE_TYPE (LightDMLayout, lightdm_layout, G_TYPE_OBJECT); + +#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LAYOUT, LightDMLayoutPrivate) + +static gboolean have_layouts = FALSE; +static Display *display = NULL; +static XklEngine *xkl_engine = NULL; +static XklConfigRec *xkl_config = NULL; +static GList *layouts = NULL; +static LightDMLayout *default_layout = NULL; + +static gchar * +make_layout_string (const gchar *layout, const gchar *variant) +{ + if (!layout || layout[0] == 0) + return NULL; + else if (!variant || variant[0] == 0) + return g_strdup (layout); + else + return g_strdup_printf ("%s\t%s", layout, variant); +} + +static void +parse_layout_string (const gchar *name, gchar **layout, gchar **variant) +{ + gchar **split; + + *layout = NULL; + *variant = NULL; + + if (!name) + return; + + split = g_strsplit (name, "\t", 2); + if (split[0]) + { + *layout = g_strdup (split[0]); + if (split[1]) + *variant = g_strdup (split[1]); + } + g_strfreev (split); +} + +static void +variant_cb (XklConfigRegistry *config, + const XklConfigItem *item, + gpointer data) +{ + LightDMLayout *layout; + gchar *full_name; + + full_name = make_layout_string (data, item->name); + + layout = g_object_new (LIGHTDM_TYPE_LAYOUT, "name", full_name, "short-description", item->short_description, "description", item->description, NULL); + layouts = g_list_append (layouts, layout); + + g_free (full_name); +} + +static void +layout_cb (XklConfigRegistry *config, + const XklConfigItem *item, + gpointer data) +{ + LightDMLayout *layout; + + layout = g_object_new (LIGHTDM_TYPE_LAYOUT, "name", item->name, "short-description", item->short_description, "description", item->description, NULL); + layouts = g_list_append (layouts, layout); + + xkl_config_registry_foreach_layout_variant (config, item->name, variant_cb, (gpointer) item->name); +} + +/** + * lightdm_get_layouts: + * + * Get a list of keyboard layouts to present to the user. + * + * Return value: (element-type LightDMLayout) (transfer none): A list of #LightDMLayout that should be presented to the user. + **/ +GList * +lightdm_get_layouts (void) +{ + XklConfigRegistry *registry; + + if (have_layouts) + return layouts; + + display = XOpenDisplay (NULL); + xkl_engine = xkl_engine_get_instance (display); + xkl_config = xkl_config_rec_new (); + if (!xkl_config_rec_get_from_server (xkl_config, xkl_engine)) + g_warning ("Failed to get Xkl configuration from server"); + + registry = xkl_config_registry_get_instance (xkl_engine); + xkl_config_registry_load (registry, FALSE); + xkl_config_registry_foreach_layout (registry, layout_cb, NULL); + g_object_unref (registry); + + have_layouts = TRUE; + + return layouts; +} + +/** + * lightdm_set_layout: + * @layout: The layout to use + * + * Set the layout for this session. + **/ +void +lightdm_set_layout (LightDMLayout *dmlayout) +{ + XklConfigRec *config; + gchar *layout, *variant; + + g_return_if_fail (dmlayout != NULL); + + g_debug ("Setting keyboard layout to '%s'", lightdm_layout_get_name (dmlayout)); + + parse_layout_string (lightdm_layout_get_name (dmlayout), &layout, &variant); + + config = xkl_config_rec_new (); + config->layouts = g_malloc (sizeof (gchar *) * 2); + config->variants = g_malloc (sizeof (gchar *) * 2); + config->model = g_strdup (xkl_config->model); + config->layouts[0] = layout; + config->layouts[1] = NULL; + config->variants[0] = variant; + config->variants[1] = NULL; + if (!xkl_config_rec_activate (config, xkl_engine)) + g_warning ("Failed to activate XKL config"); + g_object_unref (config); +} + +/** + * lightdm_get_layout: + * + * Get the current keyboard layout. + * + * Return value: (transfer none): The currently active layout for this user. + **/ +LightDMLayout * +lightdm_get_layout (void) +{ + lightdm_get_layouts (); + + if (layouts && xkl_config && !default_layout) + { + gchar *full_name; + GList *item; + + full_name = make_layout_string (xkl_config->layouts ? xkl_config->layouts[0] : NULL, + xkl_config->variants ? xkl_config->variants[0] : NULL); + + for (item = layouts; item; item = item->next) + { + LightDMLayout *iter_layout = (LightDMLayout *) item->data; + if (g_strcmp0 (lightdm_layout_get_name (iter_layout), full_name) == 0) + { + default_layout = iter_layout; + break; + } + } + + g_free (full_name); + } + + return default_layout; +} + +/** + * lightdm_layout_get_name: + * @layout: A #LightDMLayout + * + * Get the name of a layout. + * + * Return value: The name of the layout + **/ +const gchar * +lightdm_layout_get_name (LightDMLayout *layout) +{ + g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL); + return GET_PRIVATE (layout)->name; +} + +/** + * lightdm_layout_get_short_description: + * @layout: A #LightDMLayout + * + * Get the short description of a layout. + * + * Return value: A short description of the layout + **/ +const gchar * +lightdm_layout_get_short_description (LightDMLayout *layout) +{ + g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL); + return GET_PRIVATE (layout)->short_description; +} + +/** + * lightdm_layout_get_description: + * @layout: A #LightDMLayout + * + * Get the long description of a layout. + * + * Return value: A long description of the layout + **/ +const gchar * +lightdm_layout_get_description (LightDMLayout *layout) +{ + g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL); + return GET_PRIVATE (layout)->description; +} + +static void +lightdm_layout_init (LightDMLayout *layout) +{ +} + +static void +lightdm_layout_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + LightDMLayout *self = LIGHTDM_LAYOUT (object); + LightDMLayoutPrivate *priv = GET_PRIVATE (self); + + switch (prop_id) { + case PROP_NAME: + g_free (priv->name); + priv->name = g_strdup (g_value_get_string (value)); + break; + case PROP_SHORT_DESCRIPTION: + g_free (priv->short_description); + priv->short_description = g_strdup (g_value_get_string (value)); + break; + case PROP_DESCRIPTION: + g_free (priv->description); + priv->description = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_layout_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + LightDMLayout *self; + + self = LIGHTDM_LAYOUT (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, lightdm_layout_get_name (self)); + break; + case PROP_SHORT_DESCRIPTION: + g_value_set_string (value, lightdm_layout_get_short_description (self)); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, lightdm_layout_get_description (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_layout_class_init (LightDMLayoutClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LightDMLayoutPrivate)); + + object_class->set_property = lightdm_layout_set_property; + object_class->get_property = lightdm_layout_get_property; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "Name of the layout", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_SHORT_DESCRIPTION, + g_param_spec_string ("short-description", + "short-description", + "Short description of the layout", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_DESCRIPTION, + g_param_spec_string ("description", + "description", + "Long description of the layout", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c new file mode 100644 index 00000000..f7830ff2 --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2010-2011 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include "config.h" + +#include +#include + +#include "lightdm/power.h" + +static GDBusProxy *upower_proxy = NULL; +static GDBusProxy *ck_proxy = NULL; + +static gboolean +upower_call_function (const gchar *function, gboolean default_result, GError **error) +{ + GVariant *result; + gboolean function_result = FALSE; + + if (!upower_proxy) + { + upower_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.UPower", + "/org/freedesktop/UPower", + "org.freedesktop.UPower", + NULL, + error); + if (!upower_proxy) + return FALSE; + } + + result = g_dbus_proxy_call_sync (upower_proxy, + function, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (!result) + return default_result; + + if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)"))) + g_variant_get (result, "(b)", &function_result); + + g_variant_unref (result); + return function_result; +} + +/** + * lightdm_get_can_suspend: + * + * Checks if authorized to do a system suspend. + * + * Return value: #TRUE if can suspend the system + **/ +gboolean +lightdm_get_can_suspend (void) +{ + return upower_call_function ("SuspendAllowed", FALSE, NULL); +} + +/** + * lightdm_suspend: + * @error: return location for a #GError, or %NULL + * + * Triggers a system suspend. + * + * Return value: #TRUE if suspend initiated. + **/ +gboolean +lightdm_suspend (GError **error) +{ + return upower_call_function ("Suspend", TRUE, error); +} + +/** + * lightdm_get_can_hibernate: + * + * Checks if is authorized to do a system hibernate. + * + * Return value: #TRUE if can hibernate the system + **/ +gboolean +lightdm_get_can_hibernate (void) +{ + return upower_call_function ("HibernateAllowed", FALSE, NULL); +} + +/** + * lightdm_hibernate: + * @error: return location for a #GError, or %NULL + * + * Triggers a system hibernate. + * + * Return value: #TRUE if hibernate initiated. + **/ +gboolean +lightdm_hibernate (GError **error) +{ + return upower_call_function ("Hibernate", TRUE, error); +} + +static gboolean +ck_call_function (const gchar *function, gboolean default_result, GError **error) +{ + GVariant *result; + gboolean function_result = FALSE; + + if (!ck_proxy) + { + ck_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + NULL, + error); + if (!ck_proxy) + return FALSE; + } + + result = g_dbus_proxy_call_sync (ck_proxy, + function, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + + if (!result) + return default_result; + + if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)"))) + g_variant_get (result, "(b)", &function_result); + + g_variant_unref (result); + return function_result; +} + +/** + * lightdm_get_can_restart: + * + * Checks if is authorized to do a system restart. + * + * Return value: #TRUE if can restart the system + **/ +gboolean +lightdm_get_can_restart (void) +{ + return ck_call_function ("CanRestart", FALSE, NULL); +} + +/** + * lightdm_restart: + * @error: return location for a #GError, or %NULL + * + * Triggers a system restart. + * + * Return value: #TRUE if restart initiated. + **/ +gboolean +lightdm_restart (GError **error) +{ + return ck_call_function ("Restart", TRUE, error); +} + +/** + * lightdm_get_can_shutdown: + * + * Checks if is authorized to do a system shutdown. + * + * Return value: #TRUE if can shutdown the system + **/ +gboolean +lightdm_get_can_shutdown (void) +{ + return ck_call_function ("CanStop", FALSE, NULL); +} + +/** + * lightdm_shutdown: + * @error: return location for a #GError, or %NULL + * + * Triggers a system shutdown. + * + * Return value: #TRUE if shutdown initiated. + **/ +gboolean +lightdm_shutdown (GError **error) +{ + return ck_call_function ("Stop", TRUE, error); +} diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c new file mode 100644 index 00000000..2df3a1af --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2010 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include +#include + +#include "lightdm/session.h" + +enum { + PROP_0, + PROP_KEY, + PROP_NAME, + PROP_COMMENT +}; + +typedef struct +{ + gchar *key; + gchar *name; + gchar *comment; +} LightDMSessionPrivate; + +G_DEFINE_TYPE (LightDMSession, lightdm_session, G_TYPE_OBJECT); + +#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_SESSION, LightDMSessionPrivate) + +static gboolean have_sessions = FALSE; +static GList *local_sessions = NULL; +static GList *remote_sessions = NULL; + +static gint +compare_session (gconstpointer a, gconstpointer b) +{ + LightDMSessionPrivate *priv_a = GET_PRIVATE (a); + LightDMSessionPrivate *priv_b = GET_PRIVATE (b); + return strcmp (priv_a->name, priv_b->name); +} + +static LightDMSession * +load_session (GKeyFile *key_file, const gchar *key) +{ + gchar *domain, *name; + LightDMSession *session; + LightDMSessionPrivate *priv; + gchar *try_exec; + + if (g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) || + g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL)) + return NULL; + +#ifdef G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN + domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN, NULL); +#else + domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL); +#endif + name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, domain, NULL); + if (!name) + { + g_warning ("Ignoring session without name"); + g_free (domain); + return NULL; + } + + try_exec = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, domain, NULL); + if (try_exec) + { + gchar *full_path; + + full_path = g_find_program_in_path (try_exec); + g_free (try_exec); + + if (!full_path) + { + g_free (name); + g_free (domain); + return NULL; + } + g_free (full_path); + } + + session = g_object_new (LIGHTDM_TYPE_SESSION, NULL); + priv = GET_PRIVATE (session); + + g_free (priv->key); + priv->key = g_strdup (key); + + g_free (priv->name); + priv->name = name; + + g_free (priv->comment); + priv->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, domain, NULL); + if (!priv->comment) + priv->comment = g_strdup (""); + + g_free (domain); + + return session; +} + +static GList * +load_sessions (const gchar *sessions_dir) +{ + GDir *directory; + GList *sessions = NULL; + GError *error = NULL; + + directory = g_dir_open (sessions_dir, 0, &error); + if (error) + g_warning ("Failed to open sessions directory: %s", error->message); + g_clear_error (&error); + if (!directory) + return NULL; + + while (TRUE) + { + const gchar *filename; + gchar *path; + GKeyFile *key_file; + gboolean result; + + filename = g_dir_read_name (directory); + if (filename == NULL) + break; + + if (!g_str_has_suffix (filename, ".desktop")) + continue; + + path = g_build_filename (sessions_dir, filename, NULL); + + key_file = g_key_file_new (); + result = g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error); + if (error) + g_warning ("Failed to load session file %s: %s:", path, error->message); + g_clear_error (&error); + + if (result) + { + gchar *key; + LightDMSession *session; + + key = g_strndup (filename, strlen (filename) - strlen (".desktop")); + session = load_session (key_file, key); + if (session) + { + g_debug ("Loaded session %s (%s, %s)", path, GET_PRIVATE (session)->name, GET_PRIVATE (session)->comment); + sessions = g_list_insert_sorted (sessions, session, compare_session); + } + else + g_debug ("Ignoring session %s", path); + g_free (key); + } + + g_free (path); + g_key_file_free (key_file); + } + + g_dir_close (directory); + + return sessions; +} + +static void +update_sessions (void) +{ + GKeyFile *config_key_file = NULL; + gchar *config_path = NULL; + gchar *xsessions_dir; + gchar *remote_sessions_dir; + gboolean result; + GError *error = NULL; + + if (have_sessions) + return; + + xsessions_dir = g_strdup (XSESSIONS_DIR); + remote_sessions_dir = g_strdup (REMOTE_SESSIONS_DIR); + + /* Use session directory from configuration */ + /* FIXME: This should be sent in the greeter connection */ + config_path = g_build_filename (CONFIG_DIR, "lightdm.conf", NULL); + config_key_file = g_key_file_new (); + result = g_key_file_load_from_file (config_key_file, config_path, G_KEY_FILE_NONE, &error); + if (error) + g_warning ("Failed to open configuration file: %s", error->message); + g_clear_error (&error); + if (result) + { + gchar *value; + + value = g_key_file_get_string (config_key_file, "LightDM", "xsessions-directory", NULL); + if (value) + { + g_free (xsessions_dir); + xsessions_dir = value; + } + + value = g_key_file_get_string (config_key_file, "LightDM", "remote-sessions-directory", NULL); + if (value) + { + g_free (remote_sessions_dir); + remote_sessions_dir = value; + } + } + g_key_file_free (config_key_file); + g_free (config_path); + + local_sessions = load_sessions (xsessions_dir); + remote_sessions = load_sessions (remote_sessions_dir); + + g_free (xsessions_dir); + g_free (remote_sessions_dir); + + have_sessions = TRUE; +} + +/** + * lightdm_get_sessions: + * + * Get the available sessions. + * + * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession + **/ +GList * +lightdm_get_sessions (void) +{ + update_sessions (); + return local_sessions; +} + +/** + * lightdm_get_remote_sessions: + * + * Get the available remote sessions. + * + * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession + **/ +GList * +lightdm_get_remote_sessions (void) +{ + update_sessions (); + return remote_sessions; +} + +/** + * lightdm_session_get_key: + * @session: A #LightDMSession + * + * Get the key for a session + * + * Return value: The session key + **/ +const gchar * +lightdm_session_get_key (LightDMSession *session) +{ + g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL); + return GET_PRIVATE (session)->key; +} + +/** + * lightdm_session_get_name: + * @session: A #LightDMSession + * + * Get the name for a session + * + * Return value: The session name + **/ +const gchar * +lightdm_session_get_name (LightDMSession *session) +{ + g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL); + return GET_PRIVATE (session)->name; +} + +/** + * lightdm_session_get_comment: + * @session: A #LightDMSession + * + * Get the comment for a session + * + * Return value: The session comment + **/ +const gchar * +lightdm_session_get_comment (LightDMSession *session) +{ + g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL); + return GET_PRIVATE (session)->comment; +} + +static void +lightdm_session_init (LightDMSession *session) +{ +} + +static void +lightdm_session_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +lightdm_session_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + LightDMSession *self; + + self = LIGHTDM_SESSION (object); + + switch (prop_id) { + case PROP_KEY: + g_value_set_string (value, lightdm_session_get_key (self)); + break; + case PROP_NAME: + g_value_set_string (value, lightdm_session_get_name (self)); + break; + case PROP_COMMENT: + g_value_set_string (value, lightdm_session_get_comment (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_session_finalize (GObject *object) +{ + LightDMSession *self = LIGHTDM_SESSION (object); + LightDMSessionPrivate *priv = GET_PRIVATE (self); + + g_free (priv->key); + g_free (priv->name); + g_free (priv->comment); +} + +static void +lightdm_session_class_init (LightDMSessionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LightDMSessionPrivate)); + + object_class->set_property = lightdm_session_set_property; + object_class->get_property = lightdm_session_get_property; + object_class->finalize = lightdm_session_finalize; + + g_object_class_install_property (object_class, + PROP_KEY, + g_param_spec_string ("key", + "key", + "Session key", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "Session name", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_COMMENT, + g_param_spec_string ("comment", + "comment", + "Session comment", + NULL, + G_PARAM_READABLE)); +} diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c new file mode 100644 index 00000000..77f8a182 --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010-2011 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include + +#include "lightdm/system.h" + +static gchar *hostname = NULL; + +/** + * lightdm_get_hostname: + * + * Return value: The name of the host we are running on. + **/ +const gchar * +lightdm_get_hostname (void) +{ + if (!hostname) + { + struct utsname info; + uname (&info); + hostname = g_strdup (info.nodename); + } + + return hostname; +} diff --git a/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c new file mode 100644 index 00000000..8df19079 --- /dev/null +++ b/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c @@ -0,0 +1,1655 @@ +/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*- + * + * Copyright (C) 2010 Robert Ancell. + * Author: Robert Ancell + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 or version 3 of the License. + * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "lightdm/user.h" + +enum +{ + LIST_PROP_0, + LIST_PROP_NUM_USERS, + LIST_PROP_USERS, +}; + +enum +{ + USER_PROP_0, + USER_PROP_NAME, + USER_PROP_REAL_NAME, + USER_PROP_DISPLAY_NAME, + USER_PROP_HOME_DIRECTORY, + USER_PROP_IMAGE, + USER_PROP_BACKGROUND, + USER_PROP_LANGUAGE, + USER_PROP_LAYOUT, + USER_PROP_LAYOUTS, + USER_PROP_SESSION, + USER_PROP_LOGGED_IN, + USER_PROP_HAS_MESSAGES +}; + +enum +{ + USER_ADDED, + USER_CHANGED, + USER_REMOVED, + LAST_LIST_SIGNAL +}; +static guint list_signals[LAST_LIST_SIGNAL] = { 0 }; + +enum +{ + CHANGED, + LAST_USER_SIGNAL +}; +static guint user_signals[LAST_USER_SIGNAL] = { 0 }; + +typedef struct +{ + /* Connection to AccountsService */ + GDBusProxy *accounts_service_proxy; + GList *user_account_objects; + + /* Connection to DisplayManager */ + GDBusProxy *display_manager_proxy; + + /* File monitor for password file */ + GFileMonitor *passwd_monitor; + + /* TRUE if have scanned users */ + gboolean have_users; + + /* List of users */ + GList *users; + + /* List of sessions */ + GList *sessions; +} LightDMUserListPrivate; + +typedef struct +{ + GDBusProxy *proxy; + LightDMUser *user; +} UserAccountObject; + +typedef struct +{ + LightDMUserList *user_list; + + gchar *name; + gchar *real_name; + gchar *home_directory; + gchar *image; + gchar *background; + gboolean has_messages; + + GKeyFile *dmrc_file; + gchar *language; + gchar **layouts; + gchar *session; +} LightDMUserPrivate; + +typedef struct +{ + GObject parent_instance; + gchar *path; + gchar *username; +} Session; + +typedef struct +{ + GObjectClass parent_class; +} SessionClass; + +G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT); +G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT); +#define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session)) +GType session_get_type (void); +G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT); + +#define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate) +#define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate) + +#define PASSWD_FILE "/etc/passwd" +#define USER_CONFIG_FILE "/etc/lightdm/users.conf" + +static LightDMUserList *singleton = NULL; + +/** + * lightdm_user_list_get_instance: + * + * Get the user list. + * + * Return value: (transfer none): the #LightDMUserList + **/ +LightDMUserList * +lightdm_user_list_get_instance (void) +{ + if (!singleton) + singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL); + return singleton; +} + +static LightDMUser * +get_user_by_name (LightDMUserList *user_list, const gchar *username) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + GList *link; + + for (link = priv->users; link; link = link->next) + { + LightDMUser *user = link->data; + if (strcmp (lightdm_user_get_name (user), username) == 0) + return user; + } + + return NULL; +} + +static gint +compare_user (gconstpointer a, gconstpointer b) +{ + LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b; + return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b)); +} + +static gboolean +update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image) +{ + LightDMUserPrivate *priv = GET_USER_PRIVATE (user); + + if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 && + g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 && + g_strcmp0 (lightdm_user_get_image (user), image) == 0) + return FALSE; + + g_free (priv->real_name); + priv->real_name = g_strdup (real_name); + g_free (priv->home_directory); + priv->home_directory = g_strdup (home_directory); + g_free (priv->image); + priv->image = g_strdup (image); + + return TRUE; +} + +static void +user_changed_cb (LightDMUser *user, LightDMUserList *user_list) +{ + g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user); +} + +static void +load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + GKeyFile *config; + gchar *value; + gint minimum_uid; + gchar **hidden_users, **hidden_shells; + GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link; + GError *error = NULL; + + g_debug ("Loading user config from %s", USER_CONFIG_FILE); + + config = g_key_file_new (); + g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error); + if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info + g_clear_error (&error); + + if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL)) + minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL); + else + minimum_uid = 500; + + value = g_key_file_get_string (config, "UserList", "hidden-users", NULL); + if (!value) + value = g_strdup ("nobody nobody4 noaccess"); + hidden_users = g_strsplit (value, " ", -1); + g_free (value); + + value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL); + if (!value) + value = g_strdup ("/bin/false /usr/sbin/nologin"); + hidden_shells = g_strsplit (value, " ", -1); + g_free (value); + + g_key_file_free (config); + + setpwent (); + + while (TRUE) + { + struct passwd *entry; + LightDMUser *user; + LightDMUserPrivate *user_priv; + char **tokens; + gchar *real_name, *image; + int i; + + errno = 0; + entry = getpwent (); + if (!entry) + break; + + /* Ignore system users */ + if (entry->pw_uid < minimum_uid) + continue; + + /* Ignore users disabled by shell */ + if (entry->pw_shell) + { + for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++); + if (hidden_shells[i]) + continue; + } + + /* Ignore certain users */ + for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++); + if (hidden_users[i]) + continue; + + tokens = g_strsplit (entry->pw_gecos, ",", -1); + if (tokens[0] != NULL && tokens[0][0] != '\0') + real_name = g_strdup (tokens[0]); + else + real_name = g_strdup (""); + g_strfreev (tokens); + + image = g_build_filename (entry->pw_dir, ".face", NULL); + if (!g_file_test (image, G_FILE_TEST_EXISTS)) + { + g_free (image); + image = g_build_filename (entry->pw_dir, ".face.icon", NULL); + if (!g_file_test (image, G_FILE_TEST_EXISTS)) + { + g_free (image); + image = NULL; + } + } + + user = g_object_new (LIGHTDM_TYPE_USER, NULL); + user_priv = GET_USER_PRIVATE (user); + user_priv->user_list = user_list; + g_free (user_priv->name); + user_priv->name = g_strdup (entry->pw_name); + g_free (user_priv->real_name); + user_priv->real_name = real_name; + g_free (user_priv->home_directory); + user_priv->home_directory = g_strdup (entry->pw_dir); + g_free (user_priv->image); + user_priv->image = image; + + /* Update existing users if have them */ + for (link = priv->users; link; link = link->next) + { + LightDMUser *info = link->data; + if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0) + { + if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user))) + changed_users = g_list_insert_sorted (changed_users, info, compare_user); + g_object_unref (user); + user = info; + break; + } + } + if (!link) + { + /* Only notify once we have loaded the user list */ + if (priv->have_users) + new_users = g_list_insert_sorted (new_users, user, compare_user); + } + users = g_list_insert_sorted (users, user, compare_user); + } + g_strfreev (hidden_users); + g_strfreev (hidden_shells); + + if (errno != 0) + g_warning ("Failed to read password database: %s", strerror (errno)); + + endpwent (); + + /* Use new user list */ + old_users = priv->users; + priv->users = users; + + /* Notify of changes */ + for (link = new_users; link; link = link->next) + { + LightDMUser *info = link->data; + g_debug ("User %s added", lightdm_user_get_name (info)); + g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list); + if (emit_add_signal) + g_signal_emit (user_list, list_signals[USER_ADDED], 0, info); + } + g_list_free (new_users); + for (link = changed_users; link; link = link->next) + { + LightDMUser *info = link->data; + g_debug ("User %s changed", lightdm_user_get_name (info)); + g_signal_emit (info, user_signals[CHANGED], 0); + } + g_list_free (changed_users); + for (link = old_users; link; link = link->next) + { + GList *new_link; + + /* See if this user is in the current list */ + for (new_link = priv->users; new_link; new_link = new_link->next) + { + if (new_link->data == link->data) + break; + } + + if (!new_link) + { + LightDMUser *info = link->data; + g_debug ("User %s removed", lightdm_user_get_name (info)); + g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info); + g_object_unref (info); + } + } + g_list_free (old_users); +} + +static void +passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list) +{ + if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) + { + g_debug ("%s changed, reloading user list", g_file_get_path (file)); + load_passwd_file (user_list, TRUE); + } +} + +static gboolean +update_user (UserAccountObject *object) +{ + LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user); + GVariant *result, *value; + GVariantIter *iter; + gchar *name; + GError *error = NULL; + + result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy), + "org.freedesktop.Accounts", + g_dbus_proxy_get_object_path (object->proxy), + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", "org.freedesktop.Accounts.User"), + G_VARIANT_TYPE ("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (error) + g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message); + g_clear_error (&error); + if (!result) + return FALSE; + + g_variant_get (result, "(a{sv})", &iter); + while (g_variant_iter_loop (iter, "{&sv}", &name, &value)) + { + if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + { + gchar *user_name; + g_variant_get (value, "&s", &user_name); + g_free (priv->name); + priv->name = g_strdup (user_name); + } + else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + { + gchar *real_name; + g_variant_get (value, "&s", &real_name); + g_free (priv->real_name); + priv->real_name = g_strdup (real_name); + } + else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + { + gchar *home_directory; + g_variant_get (value, "&s", &home_directory); + g_free (priv->home_directory); + priv->home_directory = g_strdup (home_directory); + } + else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + { + gchar *icon_file; + g_variant_get (value, "&s", &icon_file); + g_free (priv->image); + if (strcmp (icon_file, "") == 0) + priv->image = NULL; + else + priv->image = g_strdup (icon_file); + } + else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + { + gchar *background_file; + g_variant_get (value, "&s", &background_file); + g_free (priv->background); + if (strcmp (background_file, "") == 0) + priv->background = NULL; + else + priv->background = g_strdup (background_file); + } + } + g_variant_iter_free (iter); + + g_variant_unref (result); + + return TRUE; +} + +static void +user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object) +{ + if (strcmp (signal_name, "Changed") == 0) + { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()"))) + { + g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy)); + update_user (object); + g_signal_emit (object->user, user_signals[CHANGED], 0); + } + else + g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters)); + } +} + +static UserAccountObject * +user_account_object_new (LightDMUserList *user_list, const gchar *path) +{ + GDBusProxy *proxy; + UserAccountObject *object; + GError *error = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.Accounts", + path, + "org.freedesktop.Accounts.User", + NULL, + &error); + if (error) + g_warning ("Error getting user %s: %s", path, error->message); + g_clear_error (&error); + if (!proxy) + return NULL; + + object = g_malloc0 (sizeof (UserAccountObject)); + object->user = g_object_new (LIGHTDM_TYPE_USER, NULL); + GET_USER_PRIVATE (object->user)->user_list = user_list; + object->proxy = proxy; + g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object); + + return object; +} + +static void +user_account_object_free (UserAccountObject *object) +{ + if (!object) + return; + g_object_unref (object->user); + g_object_unref (object->proxy); + g_free (object); +} + +static UserAccountObject * +find_user_account_object (LightDMUserList *user_list, const gchar *path) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + GList *link; + + for (link = priv->user_account_objects; link; link = link->next) + { + UserAccountObject *object = link->data; + if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0) + return object; + } + + return NULL; +} + +static void +user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + + if (strcmp (signal_name, "UserAdded") == 0) + { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) + { + gchar *path; + UserAccountObject *object; + + g_variant_get (parameters, "(&o)", &path); + + /* Ignore duplicate requests */ + object = find_user_account_object (user_list, path); + if (object) + return; + + object = user_account_object_new (user_list, path); + if (object && update_user (object)) + { + g_debug ("User %s added", path); + priv->user_account_objects = g_list_append (priv->user_account_objects, object); + priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user); + g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list); + g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user); + } + else + user_account_object_free (object); + } + else + g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters)); + } + else if (strcmp (signal_name, "UserDeleted") == 0) + { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) + { + gchar *path; + UserAccountObject *object; + + g_variant_get (parameters, "(&o)", &path); + + object = find_user_account_object (user_list, path); + if (!object) + return; + + g_debug ("User %s deleted", path); + priv->users = g_list_remove (priv->users, object->user); + g_object_unref (object->user); + + g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user); + + priv->user_account_objects = g_list_remove (priv->user_account_objects, object); + user_account_object_free (object); + } + else + g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters)); + } +} + +static Session * +load_session (LightDMUserList *user_list, const gchar *path) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + Session *session = NULL; + GVariant *result, *username; + GError *error = NULL; + + result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy), + "org.freedesktop.DisplayManager", + path, + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (error) + g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message); + g_clear_error (&error); + if (!result) + return NULL; + + g_variant_get (result, "(v)", &username); + if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING)) + { + gchar *name; + + g_variant_get (username, "&s", &name); + + g_debug ("Loaded session %s (%s)", path, name); + session = g_object_new (session_get_type (), NULL); + session->username = g_strdup (name); + session->path = g_strdup (path); + priv->sessions = g_list_append (priv->sessions, session); + } + g_variant_unref (username); + g_variant_unref (result); + + return session; +} + +static void +display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + + if (strcmp (signal_name, "SessionAdded") == 0) + { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) + { + gchar *path; + Session *session; + LightDMUser *user = NULL; + + g_variant_get (parameters, "(&o)", &path); + session = load_session (user_list, path); + if (session) + user = get_user_by_name (user_list, session->username); + if (user) + g_signal_emit (user, user_signals[CHANGED], 0); + } + } + else if (strcmp (signal_name, "SessionRemoved") == 0) + { + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) + { + gchar *path; + GList *link; + + g_variant_get (parameters, "(&o)", &path); + + for (link = priv->sessions; link; link = link->next) + { + Session *session = link->data; + if (strcmp (session->path, path) == 0) + { + LightDMUser *user; + + g_debug ("Session %s removed", path); + priv->sessions = g_list_remove_link (priv->sessions, link); + user = get_user_by_name (user_list, session->username); + if (user) + g_signal_emit (user, user_signals[CHANGED], 0); + g_object_unref (session); + break; + } + } + } + } +} + +static void +update_users (LightDMUserList *user_list) +{ + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list); + GError *error = NULL; + + if (priv->have_users) + return; + priv->have_users = TRUE; + + priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + "org.freedesktop.Accounts", + NULL, + &error); + if (error) + g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message); + g_clear_error (&error); + + /* Check if the service exists */ + if (priv->accounts_service_proxy) + { + gchar *name; + + name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy); + if (!name) + { + g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file"); + g_object_unref (priv->accounts_service_proxy); + priv->accounts_service_proxy = NULL; + } + g_free (name); + } + + if (priv->accounts_service_proxy) + { + GVariant *result; + + g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list); + + result = g_dbus_proxy_call_sync (priv->accounts_service_proxy, + "ListCachedUsers", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (error) + g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message); + g_clear_error (&error); + if (!result) + return; + + if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)"))) + { + GVariantIter *iter; + const gchar *path; + + g_debug ("Loading users from org.freedesktop.Accounts"); + g_variant_get (result, "(ao)", &iter); + while (g_variant_iter_loop (iter, "&o", &path)) + { + UserAccountObject *object; + + g_debug ("Loading user %s", path); + + object = user_account_object_new (user_list, path); + if (object && update_user (object)) + { + priv->user_account_objects = g_list_append (priv->user_account_objects, object); + priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user); + g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list); + } + else + user_account_object_free (object); + } + g_variant_iter_free (iter); + } + else + g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result)); + + g_variant_unref (result); + } + else + { + GFile *passwd_file; + + load_passwd_file (user_list, FALSE); + + /* Watch for changes to user list */ + + passwd_file = g_file_new_for_path (PASSWD_FILE); + priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error); + g_object_unref (passwd_file); + if (error) + g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message); + else + g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list); + g_clear_error (&error); + } + + priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.DisplayManager", + "/org/freedesktop/DisplayManager", + "org.freedesktop.DisplayManager", + NULL, + &error); + if (error) + g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message); + g_clear_error (&error); + + if (priv->display_manager_proxy) + { + GVariant *result; + + g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list); + + result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy), + "org.freedesktop.DisplayManager", + "/org/freedesktop/DisplayManager", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (error) + g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message); + g_clear_error (&error); + if (!result) + return; + + if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)"))) + { + GVariant *value; + GVariantIter *iter; + const gchar *path; + + g_variant_get (result, "(v)", &value); + + g_debug ("Loading sessions from org.freedesktop.DisplayManager"); + g_variant_get (value, "ao", &iter); + while (g_variant_iter_loop (iter, "&o", &path)) + load_session (user_list, path); + g_variant_iter_free (iter); + + g_variant_unref (value); + } + else + g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result)); + + g_variant_unref (result); + } +} + +/** + * lightdm_user_list_get_length: + * @user_list: a #LightDMUserList + * + * Return value: The number of users able to log in + **/ +gint +lightdm_user_list_get_length (LightDMUserList *user_list) +{ + g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0); + update_users (user_list); + return g_list_length (GET_LIST_PRIVATE (user_list)->users); +} + +/** + * lightdm_user_list_get_users: + * @user_list: A #LightDMUserList + * + * Get a list of users to present to the user. This list may be a subset of the + * available users and may be empty depending on the server configuration. + * + * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user. + **/ +GList * +lightdm_user_list_get_users (LightDMUserList *user_list) +{ + g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL); + update_users (user_list); + return GET_LIST_PRIVATE (user_list)->users; +} + +/** + * lightdm_user_list_get_user_by_name: + * @user_list: A #LightDMUserList + * @username: Name of user to get. + * + * Get infomation about a given user or #NULL if this user doesn't exist. + * + * Return value: (transfer none): A #LightDMUser entry for the given user. + **/ +LightDMUser * +lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username) +{ + g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL); + g_return_val_if_fail (username != NULL, NULL); + + update_users (user_list); + + return get_user_by_name (user_list, username); +} + +static void +lightdm_user_list_init (LightDMUserList *user_list) +{ +} + +static void +lightdm_user_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +lightdm_user_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + LightDMUserList *self; + + self = LIGHTDM_USER_LIST (object); + + switch (prop_id) + { + case LIST_PROP_NUM_USERS: + g_value_set_int (value, lightdm_user_list_get_length (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_user_list_finalize (GObject *object) +{ + LightDMUserList *self = LIGHTDM_USER_LIST (object); + LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self); + + if (priv->accounts_service_proxy) + g_object_unref (priv->accounts_service_proxy); + g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free); + if (priv->passwd_monitor) + g_object_unref (priv->passwd_monitor); + g_list_free_full (priv->users, g_object_unref); + g_list_free_full (priv->sessions, g_object_unref); + + G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object); +} + +static void +lightdm_user_list_class_init (LightDMUserListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LightDMUserListPrivate)); + + object_class->set_property = lightdm_user_list_set_property; + object_class->get_property = lightdm_user_list_get_property; + object_class->finalize = lightdm_user_list_finalize; + + g_object_class_install_property (object_class, + LIST_PROP_NUM_USERS, + g_param_spec_int ("num-users", + "num-users", + "Number of login users", + 0, G_MAXINT, 0, + G_PARAM_READABLE)); + /** + * LightDMUserList::user-added: + * @user_list: A #LightDMUserList + * @user: The #LightDM user that has been added. + * + * The ::user-added signal gets emitted when a user account is created. + **/ + list_signals[USER_ADDED] = + g_signal_new ("user-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMUserListClass, user_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, LIGHTDM_TYPE_USER); + + /** + * LightDMUserList::user-changed: + * @user_list: A #LightDMUserList + * @user: The #LightDM user that has been changed. + * + * The ::user-changed signal gets emitted when a user account is modified. + **/ + list_signals[USER_CHANGED] = + g_signal_new ("user-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMUserListClass, user_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, LIGHTDM_TYPE_USER); + + /** + * LightDMUserList::user-removed: + * @user_list: A #LightDMUserList + * @user: The #LightDM user that has been removed. + * + * The ::user-removed signal gets emitted when a user account is removed. + **/ + list_signals[USER_REMOVED] = + g_signal_new ("user-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMUserListClass, user_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, LIGHTDM_TYPE_USER); +} + +/** + * lightdm_user_get_name: + * @user: A #LightDMUser + * + * Get the name of a user. + * + * Return value: The name of the given user + **/ +const gchar * +lightdm_user_get_name (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + return GET_USER_PRIVATE (user)->name; +} + +/** + * lightdm_user_get_real_name: + * @user: A #LightDMUser + * + * Get the real name of a user. + * + * Return value: The real name of the given user + **/ +const gchar * +lightdm_user_get_real_name (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + return GET_USER_PRIVATE (user)->real_name; +} + +/** + * lightdm_user_get_display_name: + * @user: A #LightDMUser + * + * Get the display name of a user. + * + * Return value: The display name of the given user + **/ +const gchar * +lightdm_user_get_display_name (LightDMUser *user) +{ + LightDMUserPrivate *priv; + + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + + priv = GET_USER_PRIVATE (user); + if (strcmp (priv->real_name, "")) + return priv->real_name; + else + return priv->name; +} + +/** + * lightdm_user_get_home_directory: + * @user: A #LightDMUser + * + * Get the home directory for a user. + * + * Return value: The users home directory + */ +const gchar * +lightdm_user_get_home_directory (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + return GET_USER_PRIVATE (user)->home_directory; +} + +/** + * lightdm_user_get_image: + * @user: A #LightDMUser + * + * Get the image URI for a user. + * + * Return value: The image URI for the given user or #NULL if no URI + **/ +const gchar * +lightdm_user_get_image (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + return GET_USER_PRIVATE (user)->image; +} + +/** + * lightdm_user_get_background: + * @user: A #LightDMUser + * + * Get the background file path for a user. + * + * Return value: The background file path for the given user or #NULL if no path + **/ +const gchar * +lightdm_user_get_background (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + return GET_USER_PRIVATE (user)->background; +} + +static void +load_dmrc (LightDMUser *user) +{ + LightDMUserPrivate *priv = GET_USER_PRIVATE (user); + gchar *path; + //gboolean have_dmrc; + + if (!priv->dmrc_file) + priv->dmrc_file = g_key_file_new (); + + /* Load from the user directory */ + path = g_build_filename (priv->home_directory, ".dmrc", NULL); + /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL); + g_free (path); + + /* If no ~/.dmrc, then load from the cache */ + // FIXME + + // FIXME: Watch for changes + + /* The Language field is actually a locale, strip the codeset off it to get the language */ + if (priv->language) + g_free (priv->language); + priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL); + if (priv->language) + { + gchar *codeset = strchr (priv->language, '.'); + if (codeset) + *codeset = '\0'; + } + + if (priv->layouts) + { + g_strfreev (priv->layouts); + priv->layouts = NULL; + } + if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL)) + { + priv->layouts = g_malloc (sizeof (gchar *) * 2); + priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL); + priv->layouts[1] = NULL; + } + + if (priv->session) + g_free (priv->session); + priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL); +} + +static GVariant * +get_property (GDBusProxy *proxy, const gchar *property) +{ + GVariant *answer; + + if (!proxy) + return NULL; + + answer = g_dbus_proxy_get_cached_property (proxy, property); + + if (!answer) + { + g_warning ("Could not get accounts property %s", property); + return NULL; + } + + return answer; +} + +static gboolean +get_boolean_property (GDBusProxy *proxy, const gchar *property) +{ + GVariant *answer; + gboolean rv; + + answer = get_property (proxy, property); + if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN)) + { + g_warning ("Unexpected accounts property type for %s: %s", + property, g_variant_get_type_string (answer)); + g_variant_unref (answer); + return FALSE; + } + + rv = g_variant_get_boolean (answer); + g_variant_unref (answer); + + return rv; +} + +static gchar * +get_string_property (GDBusProxy *proxy, const gchar *property) +{ + GVariant *answer; + gchar *rv; + + answer = get_property (proxy, property); + if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING)) + { + g_warning ("Unexpected accounts property type for %s: %s", + property, g_variant_get_type_string (answer)); + g_variant_unref (answer); + return NULL; + } + + rv = g_strdup (g_variant_get_string (answer, NULL)); + if (strcmp (rv, "") == 0) + { + g_free (rv); + rv = NULL; + } + g_variant_unref (answer); + + return rv; +} + +static gchar ** +get_string_array_property (GDBusProxy *proxy, const gchar *property) +{ + GVariant *answer; + gchar **rv; + + if (!proxy) + return NULL; + + answer = g_dbus_proxy_get_cached_property (proxy, property); + + if (!answer) + { + g_warning ("Could not get accounts property %s", property); + return NULL; + } + + if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as"))) + { + g_warning ("Unexpected accounts property type for %s: %s", + property, g_variant_get_type_string (answer)); + g_variant_unref (answer); + return NULL; + } + + rv = g_variant_dup_strv (answer, NULL); + + g_variant_unref (answer); + return rv; +} + +static gboolean +load_accounts_service (LightDMUser *user) +{ + LightDMUserPrivate *priv = GET_USER_PRIVATE (user); + LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list); + UserAccountObject *account = NULL; + GList *iter; + gchar **value; + + /* First, find AccountObject proxy */ + for (iter = list_priv->user_account_objects; iter; iter = iter->next) + { + UserAccountObject *a = iter->data; + if (a->user == user) + { + account = a; + break; + } + } + if (!account) + return FALSE; + + /* We have proxy, let's grab some properties */ + if (priv->language) + g_free (priv->language); + priv->language = get_string_property (account->proxy, "Language"); + if (priv->session) + g_free (priv->session); + priv->session = get_string_property (account->proxy, "XSession"); + + value = get_string_array_property (account->proxy, "XKeyboardLayouts"); + if (value) + { + if (value[0]) + { + g_strfreev (priv->layouts); + priv->layouts = value; + } + else + g_strfreev (value); + } + + priv->has_messages = get_boolean_property (account->proxy, "XHasMessages"); + + return TRUE; +} + +/* Loads language/layout/session info for user */ +static void +load_user_values (LightDMUser *user) +{ + LightDMUserPrivate *priv = GET_USER_PRIVATE (user); + + load_dmrc (user); + load_accounts_service (user); // overrides dmrc values + + /* Ensure a few guarantees */ + if (priv->layouts == NULL) + { + priv->layouts = g_malloc (sizeof (gchar *) * 1); + priv->layouts[0] = NULL; + } +} + +/** + * lightdm_user_get_language: + * @user: A #LightDMUser + * + * Get the language for a user. + * + * Return value: The language for the given user or #NULL if using system defaults. + **/ +const gchar * +lightdm_user_get_language (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + load_user_values (user); + return GET_USER_PRIVATE (user)->language; +} + +/** + * lightdm_user_get_layout: + * @user: A #LightDMUser + * + * Get the keyboard layout for a user. + * + * Return value: The keyboard layout for the given user or #NULL if using system defaults. Copy the value if you want to use it long term. + **/ +const gchar * +lightdm_user_get_layout (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + load_user_values (user); + return GET_USER_PRIVATE (user)->layouts[0]; +} + +/** + * lightdm_user_get_layouts: + * @user: A #LightDMUser + * + * Get the configured keyboard layouts for a user. + * + * Return value: (transfer none): A NULL-terminated array of keyboard layouts for the given user. Copy the values if you want to use them long term. + **/ +const gchar * const * +lightdm_user_get_layouts (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + load_user_values (user); + return (const gchar * const *) GET_USER_PRIVATE (user)->layouts; +} + +/** + * lightdm_user_get_session: + * @user: A #LightDMUser + * + * Get the session for a user. + * + * Return value: The session for the given user or #NULL if using system defaults. + **/ +const gchar * +lightdm_user_get_session (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL); + load_user_values (user); + return GET_USER_PRIVATE (user)->session; +} + +/** + * lightdm_user_get_logged_in: + * @user: A #LightDMUser + * + * Check if a user is logged in. + * + * Return value: #TRUE if the user is currently logged in. + **/ +gboolean +lightdm_user_get_logged_in (LightDMUser *user) +{ + LightDMUserPrivate *priv = GET_USER_PRIVATE (user); + LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list); + GList *link; + + g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE); + + for (link = list_priv->sessions; link; link = link->next) + { + Session *session = link->data; + if (strcmp (session->username, priv->name) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * lightdm_user_get_has_messages: + * @user: A #LightDMUser + * + * Check if a user has waiting messages. + * + * Return value: #TRUE if the user has waiting messages. + **/ +gboolean +lightdm_user_get_has_messages (LightDMUser *user) +{ + g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE); + load_user_values (user); + return GET_USER_PRIVATE (user)->has_messages; +} + +static void +lightdm_user_init (LightDMUser *user) +{ +} + +static void +lightdm_user_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +lightdm_user_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + LightDMUser *self; + + self = LIGHTDM_USER (object); + + switch (prop_id) + { + case USER_PROP_NAME: + g_value_set_string (value, lightdm_user_get_name (self)); + break; + case USER_PROP_REAL_NAME: + g_value_set_string (value, lightdm_user_get_real_name (self)); + break; + case USER_PROP_DISPLAY_NAME: + g_value_set_string (value, lightdm_user_get_display_name (self)); + break; + case USER_PROP_HOME_DIRECTORY: + g_value_set_string (value, lightdm_user_get_home_directory (self)); + break; + case USER_PROP_IMAGE: + g_value_set_string (value, lightdm_user_get_image (self)); + break; + case USER_PROP_BACKGROUND: + g_value_set_string (value, lightdm_user_get_background (self)); + break; + case USER_PROP_LANGUAGE: + g_value_set_string (value, lightdm_user_get_language (self)); + break; + case USER_PROP_LAYOUT: + g_value_set_string (value, lightdm_user_get_layout (self)); + break; + case USER_PROP_LAYOUTS: + g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self))); + break; + case USER_PROP_SESSION: + g_value_set_string (value, lightdm_user_get_session (self)); + break; + case USER_PROP_LOGGED_IN: + g_value_set_boolean (value, lightdm_user_get_logged_in (self)); + break; + case USER_PROP_HAS_MESSAGES: + g_value_set_boolean (value, lightdm_user_get_has_messages (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +lightdm_user_finalize (GObject *object) +{ + LightDMUser *self = LIGHTDM_USER (object); + LightDMUserPrivate *priv = GET_USER_PRIVATE (self); + + g_free (priv->name); + g_free (priv->real_name); + g_free (priv->home_directory); + g_free (priv->image); + g_free (priv->background); + g_strfreev (priv->layouts); + if (priv->dmrc_file) + g_key_file_free (priv->dmrc_file); +} + +static void +lightdm_user_class_init (LightDMUserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LightDMUserPrivate)); + + object_class->set_property = lightdm_user_set_property; + object_class->get_property = lightdm_user_get_property; + object_class->finalize = lightdm_user_finalize; + + g_object_class_install_property (object_class, + USER_PROP_NAME, + g_param_spec_string ("name", + "name", + "Username", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + USER_PROP_REAL_NAME, + g_param_spec_string ("real-name", + "real-name", + "Users real name", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + USER_PROP_DISPLAY_NAME, + g_param_spec_string ("display-name", + "display-name", + "Users display name", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + USER_PROP_HOME_DIRECTORY, + g_param_spec_string ("home-directory", + "home-directory", + "Home directory", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + USER_PROP_IMAGE, + g_param_spec_string ("image", + "image", + "Avatar image", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + USER_PROP_BACKGROUND, + g_param_spec_string ("background", + "background", + "User background", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + USER_PROP_LANGUAGE, + g_param_spec_string ("language", + "language", + "Language used by this user", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + USER_PROP_LAYOUT, + g_param_spec_string ("layout", + "layout", + "Keyboard layout used by this user", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + USER_PROP_LAYOUTS, + g_param_spec_boxed ("layouts", + "layouts", + "Keyboard layouts used by this user", + G_TYPE_STRV, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + USER_PROP_SESSION, + g_param_spec_string ("session", + "session", + "Session used by this user", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + USER_PROP_LOGGED_IN, + g_param_spec_boolean ("logged-in", + "logged-in", + "TRUE if the user is currently in a session", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + USER_PROP_LOGGED_IN, + g_param_spec_boolean ("has-messages", + "has-messages", + "TRUE if the user is has waiting messages", + FALSE, + G_PARAM_READWRITE)); + + /** + * LightDMUser::changed: + * @user: A #LightDMUser + * + * The ::changed signal gets emitted this user account is modified. + **/ + user_signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LightDMUserClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +session_init (Session *session) +{ +} + +static void +session_finalize (GObject *object) +{ + Session *self = SESSION (object); + + g_free (self->path); + g_free (self->username); +} + +static void +session_class_init (SessionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = session_finalize; +} -- cgit v1.2.3