/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
 *
 * Copyright (C) 2010 Robert Ancell.
 * Copyright (C) 2014 Canonical, Ltd.
 * Authors: Robert Ancell <robert.ancell@canonical.com>
 *          Michael Terry <michael.terry@canonical.com>
 *
 * 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.
 */

#include <config.h>

#include "user-list.h"
#include "lightdm/user.h"

/**
 * SECTION:user-list
 * @short_description: Get information on user accounts on this system
 * @include: lightdm.h
 *
 * An object that contains information about local user accounts.
 */

/**
 * SECTION:user
 * @short_description: Get information on a user account
 * @include: lightdm.h
 *
 * Information about a local user account.
 */

/**
 * LightDMUserList:
 *
 * #LightDMUserList is an opaque data structure and can only be accessed
 * using the provided functions.
 */

/**
 * LightDMUserListClass:
 *
 * Class structure for #LightDMUserList.
 */

/**
 * LightDMUser:
 *
 * #LightDMUser is an opaque data structure and can only be accessed
 * using the provided functions.
 */

/**
 * LightDMUserClass:
 *
 * Class structure for #LightDMUser.
 */

enum
{
    LIST_PROP_NUM_USERS = 1,
    LIST_PROP_LENGTH,  
    LIST_PROP_USERS,
};

enum
{
    USER_PROP_COMMON_USER = 1,
    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,
    USER_PROP_UID,
    USER_PROP_IS_LOCKED,
};

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
{
    gboolean initialized;

    /* Wrapper list, kept locally to preserve transfer-none promises */
    GList *lightdm_list;
} LightDMUserListPrivate;

typedef struct
{
    CommonUser *common_user;
} LightDMUserPrivate;

G_DEFINE_TYPE_WITH_PRIVATE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT)
G_DEFINE_TYPE_WITH_PRIVATE (LightDMUser, lightdm_user, G_TYPE_OBJECT)

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 void
user_changed_cb (CommonUser *common_user, LightDMUser *lightdm_user)
{
    g_signal_emit (lightdm_user, user_signals[CHANGED], 0);
}

static LightDMUser *
wrap_common_user (CommonUser *user)
{
    LightDMUser *lightdm_user = g_object_new (LIGHTDM_TYPE_USER, "common-user", user, NULL);
    g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), lightdm_user);
    return lightdm_user;
}

static void
user_list_added_cb (CommonUserList *common_list, CommonUser *common_user, LightDMUserList *user_list)
{
    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);
    GList *common_users = common_user_list_get_users (common_list);
    LightDMUser *lightdm_user = wrap_common_user (common_user);
    priv->lightdm_list = g_list_insert (priv->lightdm_list, lightdm_user, g_list_index (common_users, common_user));
    g_signal_emit (user_list, list_signals[USER_ADDED], 0, lightdm_user);
}

static void
user_list_changed_cb (CommonUserList *common_list, CommonUser *common_user, LightDMUserList *user_list)
{
    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);
    GList *common_users = common_user_list_get_users (common_list);
    LightDMUser *lightdm_user = g_list_nth_data (priv->lightdm_list, g_list_index (common_users, common_user));
    g_signal_emit (user_list, list_signals[USER_CHANGED], 0, lightdm_user);
}

static void
user_list_removed_cb (CommonUserList *common_list, CommonUser *common_user, LightDMUserList *user_list)
{
    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);

    for (GList *link = priv->lightdm_list; link; link = link->next)
    {
        LightDMUser *lightdm_user = link->data;
        LightDMUserPrivate *user_priv = lightdm_user_get_instance_private (lightdm_user);
        if (user_priv->common_user == common_user)
        {
            priv->lightdm_list = g_list_delete_link (priv->lightdm_list, link);
            g_signal_emit (user_list, list_signals[USER_REMOVED], 0, lightdm_user);
            g_object_unref (lightdm_user);
            break;
        }
    }
}

static void
initialize_user_list_if_needed (LightDMUserList *user_list)
{
    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);

    if (priv->initialized)
        return;

    GList *common_users = common_user_list_get_users (common_user_list_get_instance ());
    for (GList *link = common_users; link; link = link->next)
    {
        CommonUser *user = link->data;
        LightDMUser *lightdm_user = wrap_common_user (user);
        priv->lightdm_list = g_list_prepend (priv->lightdm_list, lightdm_user);
    }
    priv->lightdm_list = g_list_reverse (priv->lightdm_list);

    CommonUserList *common_list = common_user_list_get_instance ();
    g_signal_connect (common_list, USER_LIST_SIGNAL_USER_ADDED, G_CALLBACK (user_list_added_cb), user_list);
    g_signal_connect (common_list, USER_LIST_SIGNAL_USER_CHANGED, G_CALLBACK (user_list_changed_cb), user_list);
    g_signal_connect (common_list, USER_LIST_SIGNAL_USER_REMOVED, G_CALLBACK (user_list_removed_cb), user_list);

    priv->initialized = TRUE;
}

/**
 * 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);

    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);
    initialize_user_list_if_needed (user_list);
    return g_list_length (priv->lightdm_list);
}

/**
 * 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);

    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);
    initialize_user_list_if_needed (user_list);
    return priv->lightdm_list;
}

/**
 * lightdm_user_list_get_user_by_name:
 * @user_list: A #LightDMUserList
 * @username: Name of user to get.
 *
 * Get information 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);

    LightDMUserListPrivate *priv = lightdm_user_list_get_instance_private (user_list);

    initialize_user_list_if_needed (user_list);

    for (GList *link = priv->lightdm_list; link; link = link->next)
    {
        LightDMUser *user = link->data;
        if (g_strcmp0 (lightdm_user_get_name (user), username) == 0)
            return user;
    }

    return NULL;
}

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 = LIGHTDM_USER_LIST (object);

    switch (prop_id)
    {
    case LIST_PROP_NUM_USERS:
    case LIST_PROP_LENGTH:      
        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 = lightdm_user_list_get_instance_private (self);

    g_list_free_full (priv->lightdm_list, 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);

    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_DEPRECATED | G_PARAM_READABLE));

    g_object_class_install_property (object_class,
                                     LIST_PROP_LENGTH,
                                     g_param_spec_int ("length",
                                                       "length",
                                                       "Number of login users",
                                                       0, G_MAXINT, 0,
                                                       G_PARAM_READABLE));

    /*g_object_class_install_property (object_class,
                                     LIST_PROP_USERS,
                                     g_param_spec_int ("users",
                                                       "users",
                                                       "Users to present to user",
                                                       0, G_MAXINT, 0,
                                                       G_PARAM_READABLE));*/
    /**
     * LightDMUserList::user-added:
     * @user_list: A #LightDMUserList
     * @user: The #LightDMUser that has been added.
     *
     * The ::user-added signal gets emitted when a user account is created.
     **/
    list_signals[USER_ADDED] =
        g_signal_new (LIGHTDM_USER_LIST_SIGNAL_USER_ADDED,
                      G_TYPE_FROM_CLASS (klass),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (LightDMUserListClass, user_added),
                      NULL, NULL,
                      NULL,
                      G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);

    /**
     * LightDMUserList::user-changed:
     * @user_list: A #LightDMUserList
     * @user: The #LightDMUser that has been changed.
     *
     * The ::user-changed signal gets emitted when a user account is modified.
     **/
    list_signals[USER_CHANGED] =
        g_signal_new (LIGHTDM_USER_LIST_SIGNAL_USER_CHANGED,
                      G_TYPE_FROM_CLASS (klass),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
                      NULL, NULL,
                      NULL,
                      G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);

    /**
     * LightDMUserList::user-removed:
     * @user_list: A #LightDMUserList
     * @user: The #LightDMUser that has been removed.
     *
     * The ::user-removed signal gets emitted when a user account is removed.
     **/
    list_signals[USER_REMOVED] =
        g_signal_new (LIGHTDM_USER_LIST_SIGNAL_USER_REMOVED,
                      G_TYPE_FROM_CLASS (klass),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
                      NULL, NULL,
                      NULL,
                      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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_name (priv->common_user);
}

/**
 * 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_real_name (priv->common_user);
}

/**
 * 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)
{
    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_display_name (priv->common_user);
}

/**
 * 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_home_directory (priv->common_user);
}

/**
 * lightdm_user_get_image:
 * @user: A #LightDMUser
 *
 * Get the image URI for a user.
 *
 * Return value: (nullable): 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_image (priv->common_user);
}

/**
 * lightdm_user_get_background:
 * @user: A #LightDMUser
 *
 * Get the background file path for a user.
 *
 * Return value: (nullable): 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_background (priv->common_user);
}

/**
 * lightdm_user_get_language:
 * @user: A #LightDMUser
 *
 * Get the language for a user.
 *
 * Return value: (nullable): The language in the form of a local specification (e.g. "de_DE.UTF-8") for the given user or #NULL if using the system default locale.
 **/
const gchar *
lightdm_user_get_language (LightDMUser *user)
{
    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_language (priv->common_user);
}

/**
 * lightdm_user_get_layout:
 * @user: A #LightDMUser
 *
 * Get the keyboard layout for a user.
 *
 * Return value: (nullable): 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_layout (priv->common_user);
}

/**
 * lightdm_user_get_layouts:
 * @user: A #LightDMUser
 *
 * Get the configured keyboard layouts for a user.
 *
 * Return value: (transfer none) (array zero-terminated=1): 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_layouts (priv->common_user);
}

/**
 * lightdm_user_get_session:
 * @user: A #LightDMUser
 *
 * Get the session for a user.
 *
 * Return value: (nullable): 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_session (priv->common_user);
}

/**
 * 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)
{
    g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_logged_in (priv->common_user);
}

/**
 * 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);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_has_messages (priv->common_user);
}

/**
 * lightdm_user_get_uid:
 * @user: A #LightDMUser
 *
 * Get the uid of a user.
 *
 * Return value: The uid of the given user
 **/
uid_t
lightdm_user_get_uid (LightDMUser *user)
{
    g_return_val_if_fail (LIGHTDM_IS_USER (user), (uid_t)-1);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_uid (priv->common_user);
}

/**
 * lightdm_user_get_is_locked:
 * @user: A #LightDMUser
 *
 * Get if the user is locked.
 *
 * Returns: %TRUE if the user is locked
 **/
gboolean
lightdm_user_get_is_locked (LightDMUser *user)
{
    g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);

    LightDMUserPrivate *priv = lightdm_user_get_instance_private (user);
    return common_user_get_is_locked (priv->common_user);
}

static void
lightdm_user_init (LightDMUser *user)
{
}

static void
lightdm_user_set_property (GObject    *object,
                           guint       prop_id,
                           const GValue *value,
                           GParamSpec *pspec)
{
    LightDMUser *self = LIGHTDM_USER (object);
    LightDMUserPrivate *priv = lightdm_user_get_instance_private (self);

    switch (prop_id)
    {
    case USER_PROP_COMMON_USER:
        priv->common_user = g_value_dup_object (value);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
lightdm_user_get_property (GObject    *object,
                           guint       prop_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
    LightDMUser *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;
    case USER_PROP_UID:
        g_value_set_uint64 (value, lightdm_user_get_uid (self));
        break;
    case USER_PROP_IS_LOCKED:
        g_value_set_boolean (value, lightdm_user_get_is_locked (self));
    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 = lightdm_user_get_instance_private (self);

    g_object_unref (priv->common_user);

    G_OBJECT_CLASS (lightdm_user_parent_class)->finalize (object);
}

static void
lightdm_user_class_init (LightDMUserClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    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_COMMON_USER,
                                     g_param_spec_object ("common-user",
                                                          "common-user",
                                                          "Internal user object",
                                                          COMMON_TYPE_USER,
                                                          G_PARAM_CONSTRUCT_ONLY|G_PARAM_WRITABLE));
    g_object_class_install_property (object_class,
                                     USER_PROP_NAME,
                                     g_param_spec_string ("name",
                                                          "name",
                                                          "Username",
                                                          NULL,
                                                          G_PARAM_READABLE));
    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_READABLE));
    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_READABLE));
    g_object_class_install_property (object_class,
                                     USER_PROP_IMAGE,
                                     g_param_spec_string ("image",
                                                          "image",
                                                          "Avatar image",
                                                          NULL,
                                                          G_PARAM_READABLE));
    g_object_class_install_property (object_class,
                                     USER_PROP_BACKGROUND,
                                     g_param_spec_string ("background",
                                                          "background",
                                                          "User background",
                                                          NULL,
                                                          G_PARAM_READABLE));
    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_READABLE));
    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_READABLE));
    g_object_class_install_property (object_class,
                                     USER_PROP_UID,
                                     g_param_spec_uint64 ("uid",
                                                          "uid",
                                                          "User UID",
                                                          0, G_MAXUINT64, 0,
                                                          G_PARAM_READABLE));
    g_object_class_install_property (object_class,
                                     USER_PROP_IS_LOCKED,
                                     g_param_spec_boolean ("is-locked",
                                                           "is-locked",
                                                           "TRUE if the user is currently locked",
                                                           FALSE,
                                                           G_PARAM_READABLE));

    /**
     * LightDMUser::changed:
     * @user: A #LightDMUser
     *
     * The ::changed signal gets emitted this user account is modified.
     **/
    user_signals[CHANGED] =
        g_signal_new (LIGHTDM_SIGNAL_USER_CHANGED,
                      G_TYPE_FROM_CLASS (klass),
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (LightDMUserClass, changed),
                      NULL, NULL,
                      NULL,
                      G_TYPE_NONE, 0);
}
