gnome-shell/src/gdmuser/gdm-user.c

1093 lines
32 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
* Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <config.h>
#include <float.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dbus/dbus-glib.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include "gdm-user-private.h"
#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
#define GDM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_USER))
#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
#define GLOBAL_FACEDIR DATADIR "/faces"
#define MAX_FILE_SIZE 65536
#define ACCOUNTS_NAME "org.freedesktop.Accounts"
#define ACCOUNTS_USER_INTERFACE "org.freedesktop.Accounts.User"
enum {
PROP_0,
PROP_IS_LOADED
};
enum {
CHANGED,
SESSIONS_CHANGED,
LAST_SIGNAL
};
struct _GdmUser {
GObject parent;
DBusGConnection *connection;
DBusGProxy *accounts_proxy;
DBusGProxy *object_proxy;
DBusGProxyCall *get_all_call;
char *object_path;
uid_t uid;
char *user_name;
char *real_name;
char *icon_file;
GList *sessions;
gulong login_frequency;
guint is_loaded : 1;
};
struct _GdmUserClass
{
GObjectClass parent_class;
};
static void gdm_user_finalize (GObject *object);
static gboolean check_user_file (const char *filename);
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (GdmUser, gdm_user, G_TYPE_OBJECT)
static int
session_compare (const char *a,
const char *b)
{
if (a == NULL) {
return 1;
} else if (b == NULL) {
return -1;
}
return strcmp (a, b);
}
void
_gdm_user_add_session (GdmUser *user,
const char *ssid)
{
GList *li;
g_return_if_fail (GDM_IS_USER (user));
g_return_if_fail (ssid != NULL);
li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
if (li == NULL) {
g_debug ("GdmUser: adding session %s", ssid);
user->sessions = g_list_prepend (user->sessions, g_strdup (ssid));
g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
} else {
g_debug ("GdmUser: session already present: %s", ssid);
}
}
void
_gdm_user_remove_session (GdmUser *user,
const char *ssid)
{
GList *li;
g_return_if_fail (GDM_IS_USER (user));
g_return_if_fail (ssid != NULL);
li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
if (li != NULL) {
g_debug ("GdmUser: removing session %s", ssid);
g_free (li->data);
user->sessions = g_list_delete_link (user->sessions, li);
g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
} else {
g_debug ("GdmUser: session not found: %s", ssid);
}
}
guint
gdm_user_get_num_sessions (GdmUser *user)
{
return g_list_length (user->sessions);
}
static void
gdm_user_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdm_user_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdmUser *user;
user = GDM_USER (object);
switch (prop_id) {
case PROP_IS_LOADED:
g_value_set_boolean (value, user->is_loaded);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdm_user_class_init (GdmUserClass *class)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = gdm_user_finalize;
gobject_class->set_property = gdm_user_set_property;
gobject_class->get_property = gdm_user_get_property;
g_object_class_install_property (gobject_class,
PROP_IS_LOADED,
g_param_spec_boolean ("is-loaded",
NULL,
NULL,
FALSE,
G_PARAM_READABLE));
signals [CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals [SESSIONS_CHANGED] =
g_signal_new ("sessions-changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
gdm_user_init (GdmUser *user)
{
GError *error;
user->user_name = NULL;
user->real_name = NULL;
user->sessions = NULL;
error = NULL;
user->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (user->connection == NULL) {
g_warning ("Couldn't connect to system bus: %s", error->message);
}
}
static void
gdm_user_finalize (GObject *object)
{
GdmUser *user;
user = GDM_USER (object);
g_free (user->user_name);
g_free (user->real_name);
g_free (user->icon_file);
g_free (user->object_path);
if (user->accounts_proxy != NULL) {
g_object_unref (user->accounts_proxy);
}
if (user->object_proxy != NULL) {
g_object_unref (user->object_proxy);
}
if (user->connection != NULL) {
dbus_g_connection_unref (user->connection);
}
if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
(*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
}
static void
set_is_loaded (GdmUser *user,
gboolean is_loaded)
{
if (user->is_loaded != is_loaded) {
user->is_loaded = is_loaded;
g_object_notify (G_OBJECT (user), "is-loaded");
}
}
/**
* _gdm_user_update_from_pwent:
* @user: the user object to update.
* @pwent: the user data to use.
*
* Updates the properties of @user using the data in @pwent.
**/
void
_gdm_user_update_from_pwent (GdmUser *user,
const struct passwd *pwent)
{
gchar *real_name = NULL;
gboolean changed;
g_return_if_fail (GDM_IS_USER (user));
g_return_if_fail (pwent != NULL);
g_return_if_fail (user->object_path == NULL);
changed = FALSE;
/* Display Name */
if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
gchar *first_comma = NULL;
gchar *valid_utf8_name = NULL;
if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
valid_utf8_name = pwent->pw_gecos;
first_comma = strchr (valid_utf8_name, ',');
} else {
g_warning ("User %s has invalid UTF-8 in GECOS field. "
"It would be a good thing to check /etc/passwd.",
pwent->pw_name ? pwent->pw_name : "");
}
if (first_comma) {
real_name = g_strndup (valid_utf8_name,
(first_comma - valid_utf8_name));
} else if (valid_utf8_name) {
real_name = g_strdup (valid_utf8_name);
} else {
real_name = NULL;
}
if (real_name && real_name[0] == '\0') {
g_free (real_name);
real_name = NULL;
}
} else {
real_name = NULL;
}
if (g_strcmp0 (real_name, user->real_name) != 0) {
g_free (user->real_name);
user->real_name = real_name;
changed = TRUE;
} else {
g_free (real_name);
}
/* UID */
if (pwent->pw_uid != user->uid) {
user->uid = pwent->pw_uid;
changed = TRUE;
}
/* Username */
if (g_strcmp0 (pwent->pw_name, user->user_name) != 0) {
g_free (user->icon_file);
user->icon_file = NULL;
if (pwent->pw_name != NULL) {
gboolean res;
user->icon_file = g_build_filename (GDM_CACHE_DIR, pwent->pw_name, "face", NULL);
res = check_user_file (user->icon_file);
if (!res) {
g_free (user->icon_file);
user->icon_file = g_build_filename (GLOBAL_FACEDIR, pwent->pw_name, NULL);
}
}
g_free (user->user_name);
user->user_name = g_strdup (pwent->pw_name);
changed = TRUE;
}
if (!user->is_loaded) {
set_is_loaded (user, TRUE);
}
if (changed) {
g_signal_emit (user, signals[CHANGED], 0);
}
}
/**
* _gdm_user_update_login_frequency:
* @user: the user object to update
* @login_frequency: the number of times the user has logged in
*
* Updates the login frequency of @user
**/
void
_gdm_user_update_login_frequency (GdmUser *user,
guint64 login_frequency)
{
g_return_if_fail (GDM_IS_USER (user));
if (login_frequency == user->login_frequency) {
return;
}
user->login_frequency = login_frequency;
g_signal_emit (user, signals[CHANGED], 0);
}
/**
* gdm_user_get_uid:
* @user: the user object to examine.
*
* Retrieves the ID of @user.
*
* Returns: (transfer none): a pointer to an array of characters which must not be modified or
* freed, or %NULL.
**/
gulong
gdm_user_get_uid (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), -1);
return user->uid;
}
/**
* gdm_user_get_real_name:
* @user: the user object to examine.
*
* Retrieves the display name of @user.
*
* Returns: (transfer none): a pointer to an array of characters which must not be modified or
* freed, or %NULL.
**/
const char *
gdm_user_get_real_name (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), NULL);
return (user->real_name ? user->real_name : user->user_name);
}
/**
* gdm_user_get_user_name:
* @user: the user object to examine.
*
* Retrieves the login name of @user.
*
* Returns: (transfer none): a pointer to an array of characters which must not be modified or
* freed, or %NULL.
**/
const char *
gdm_user_get_user_name (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), NULL);
return user->user_name;
}
/**
* gdm_user_get_login_frequency:
* @user: a #GdmUser
*
* Returns the number of times @user has logged in.
*
* Returns: the login frequency
*/
gulong
gdm_user_get_login_frequency (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), 0);
return user->login_frequency;
}
int
gdm_user_collate (GdmUser *user1,
GdmUser *user2)
{
const char *str1;
const char *str2;
gulong num1;
gulong num2;
guint len1;
guint len2;
g_return_val_if_fail (GDM_IS_USER (user1), 0);
g_return_val_if_fail (GDM_IS_USER (user2), 0);
num1 = user1->login_frequency;
num2 = user2->login_frequency;
if (num1 > num2) {
return -1;
}
if (num1 < num2) {
return 1;
}
len1 = g_list_length (user1->sessions);
len2 = g_list_length (user2->sessions);
if (len1 > len2) {
return -1;
}
if (len1 < len2) {
return 1;
}
/* if login frequency is equal try names */
if (user1->real_name != NULL) {
str1 = user1->real_name;
} else {
str1 = user1->user_name;
}
if (user2->real_name != NULL) {
str2 = user2->real_name;
} else {
str2 = user2->user_name;
}
if (str1 == NULL && str2 != NULL) {
return -1;
}
if (str1 != NULL && str2 == NULL) {
return 1;
}
if (str1 == NULL && str2 == NULL) {
return 0;
}
return g_utf8_collate (str1, str2);
}
static gboolean
check_user_file (const char *filename)
{
gssize max_file_size = MAX_FILE_SIZE;
struct stat fileinfo;
/* Exists/Readable? */
if (stat (filename, &fileinfo) < 0) {
return FALSE;
}
/* Is a regular file */
if (G_UNLIKELY (!S_ISREG (fileinfo.st_mode))) {
return FALSE;
}
/* Size is kosher? */
if (G_UNLIKELY (fileinfo.st_size > max_file_size)) {
return FALSE;
}
return TRUE;
}
static void
rounded_rectangle (cairo_t *cr,
gdouble aspect,
gdouble x,
gdouble y,
gdouble corner_radius,
gdouble width,
gdouble height)
{
gdouble radius;
gdouble degrees;
radius = corner_radius / aspect;
degrees = G_PI / 180.0;
cairo_new_sub_path (cr);
cairo_arc (cr,
x + width - radius,
y + radius,
radius,
-90 * degrees,
0 * degrees);
cairo_arc (cr,
x + width - radius,
y + height - radius,
radius,
0 * degrees,
90 * degrees);
cairo_arc (cr,
x + radius,
y + height - radius,
radius,
90 * degrees,
180 * degrees);
cairo_arc (cr,
x + radius,
y + radius,
radius,
180 * degrees,
270 * degrees);
cairo_close_path (cr);
}
static cairo_surface_t *
surface_from_pixbuf (GdkPixbuf *pixbuf)
{
cairo_surface_t *surface;
cairo_t *cr;
surface = cairo_image_surface_create (gdk_pixbuf_get_has_alpha (pixbuf) ?
CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf));
cr = cairo_create (surface);
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
return surface;
}
/**
* go_cairo_convert_data_to_pixbuf:
* @src: a pointer to pixel data in cairo format
* @dst: a pointer to pixel data in pixbuf format
* @width: image width
* @height: image height
* @rowstride: data rowstride
*
* Converts the pixel data stored in @src in CAIRO_FORMAT_ARGB32 cairo format
* to GDK_COLORSPACE_RGB pixbuf format and move them
* to @dst. If @src == @dst, pixel are converted in place.
**/
static void
go_cairo_convert_data_to_pixbuf (unsigned char *dst,
unsigned char const *src,
int width,
int height,
int rowstride)
{
int i,j;
unsigned int t;
unsigned char a, b, c;
g_return_if_fail (dst != NULL);
#define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END
if (src == dst || src == NULL) {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
MULT(a, dst[2], dst[3], t);
MULT(b, dst[1], dst[3], t);
MULT(c, dst[0], dst[3], t);
dst[0] = a;
dst[1] = b;
dst[2] = c;
#else
MULT(a, dst[1], dst[0], t);
MULT(b, dst[2], dst[0], t);
MULT(c, dst[3], dst[0], t);
dst[3] = dst[0];
dst[0] = a;
dst[1] = b;
dst[2] = c;
#endif
dst += 4;
}
dst += rowstride - width * 4;
}
} else {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
MULT(dst[0], src[2], src[3], t);
MULT(dst[1], src[1], src[3], t);
MULT(dst[2], src[0], src[3], t);
dst[3] = src[3];
#else
MULT(dst[0], src[1], src[0], t);
MULT(dst[1], src[2], src[0], t);
MULT(dst[2], src[3], src[0], t);
dst[3] = src[0];
#endif
src += 4;
dst += 4;
}
src += rowstride - width * 4;
dst += rowstride - width * 4;
}
}
#undef MULT
}
static void
cairo_to_pixbuf (guint8 *src_data,
GdkPixbuf *dst_pixbuf)
{
unsigned char *src;
unsigned char *dst;
guint w;
guint h;
guint rowstride;
w = gdk_pixbuf_get_width (dst_pixbuf);
h = gdk_pixbuf_get_height (dst_pixbuf);
rowstride = gdk_pixbuf_get_rowstride (dst_pixbuf);
dst = gdk_pixbuf_get_pixels (dst_pixbuf);
src = src_data;
go_cairo_convert_data_to_pixbuf (dst, src, w, h, rowstride);
}
static GdkPixbuf *
frame_pixbuf (GdkPixbuf *source)
{
GdkPixbuf *dest;
cairo_t *cr;
cairo_surface_t *surface;
guint w;
guint h;
guint rowstride;
int frame_width;
double radius;
guint8 *data;
frame_width = 2;
w = gdk_pixbuf_get_width (source) + frame_width * 2;
h = gdk_pixbuf_get_height (source) + frame_width * 2;
radius = w / 10;
dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
TRUE,
8,
w,
h);
rowstride = gdk_pixbuf_get_rowstride (dest);
data = g_new0 (guint8, h * rowstride);
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
w,
h,
rowstride);
cr = cairo_create (surface);
cairo_surface_destroy (surface);
/* set up image */
cairo_rectangle (cr, 0, 0, w, h);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
cairo_fill (cr);
rounded_rectangle (cr,
1.0,
frame_width + 0.5,
frame_width + 0.5,
radius,
w - frame_width * 2 - 1,
h - frame_width * 2 - 1);
cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
cairo_fill_preserve (cr);
surface = surface_from_pixbuf (source);
cairo_set_source_surface (cr, surface, frame_width, frame_width);
cairo_fill (cr);
cairo_surface_destroy (surface);
cairo_to_pixbuf (data, dest);
cairo_destroy (cr);
g_free (data);
return dest;
}
/**
* gdm_user_is_logged_in:
* @user: a #GdmUser
*
* Returns whether or not #GdmUser is currently logged in.
*
* Returns: %TRUE or %FALSE
*/
gboolean
gdm_user_is_logged_in (GdmUser *user)
{
return user->sessions != NULL;
}
/**
* gdm_user_render_icon:
* @user: a #GdmUser
* @icon_size: the size to render the icon at
*
* Returns a #GdkPixbuf of the account icon belonging to @user
* at the pixel size specified by @icon_size.
*
* Returns: (transfer full): a #GdkPixbuf
*/
GdkPixbuf *
gdm_user_render_icon (GdmUser *user,
gint icon_size)
{
GdkPixbuf *pixbuf;
GdkPixbuf *framed;
gboolean res;
GError *error;
g_return_val_if_fail (GDM_IS_USER (user), NULL);
g_return_val_if_fail (icon_size > 12, NULL);
pixbuf = NULL;
if (user->icon_file) {
res = check_user_file (user->icon_file);
if (res) {
pixbuf = gdk_pixbuf_new_from_file_at_size (user->icon_file,
icon_size,
icon_size,
NULL);
} else {
pixbuf = NULL;
}
}
if (pixbuf != NULL) {
goto out;
}
error = NULL;
pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
"avatar-default",
icon_size,
GTK_ICON_LOOKUP_FORCE_SIZE,
&error);
if (error) {
g_warning ("%s", error->message);
g_error_free (error);
}
out:
if (pixbuf != NULL) {
framed = frame_pixbuf (pixbuf);
if (framed != NULL) {
g_object_unref (pixbuf);
pixbuf = framed;
}
}
return pixbuf;
}
/**
* gdm_user_get_icon_file:
* @user: a #GdmUser
*
* Returns the path to the account icon belonging to @user.
*
* Returns: (transfer none): a path to an icon
*/
const char *
gdm_user_get_icon_file (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), NULL);
return user->icon_file;
}
/**
* gdm_user_get_object_path:
* @user: a #GdmUser
*
* Returns the user accounts service object path of @user,
* or %NULL if @user doesn't have an object path associated
* with it.
*
* Returns: (transfer none): the primary ConsoleKit session id of the user
*/
const char *
gdm_user_get_object_path (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), NULL);
return user->object_path;
}
/**
* gdm_user_get_primary_session_id:
* @user: a #GdmUser
*
* Returns the primary ConsoleKit session id of @user, or %NULL if @user isn't
* logged in.
*
* Returns: (transfer none): the primary ConsoleKit session id of the user
*/
const char *
gdm_user_get_primary_session_id (GdmUser *user)
{
if (!gdm_user_is_logged_in (user)) {
g_debug ("User %s is not logged in, so has no primary session",
gdm_user_get_user_name (user));
return NULL;
}
/* FIXME: better way to choose? */
return user->sessions->data;
}
static void
collect_props (const gchar *key,
const GValue *value,
GdmUser *user)
{
gboolean handled = TRUE;
if (strcmp (key, "Uid") == 0) {
user->uid = g_value_get_uint64 (value);
} else if (strcmp (key, "UserName") == 0) {
g_free (user->user_name);
user->user_name = g_value_dup_string (value);
} else if (strcmp (key, "RealName") == 0) {
g_free (user->real_name);
user->real_name = g_value_dup_string (value);
} else if (strcmp (key, "AccountType") == 0) {
/* ignore */
} else if (strcmp (key, "Email") == 0) {
/* ignore */
} else if (strcmp (key, "Language") == 0) {
/* ignore */
} else if (strcmp (key, "Location") == 0) {
/* ignore */
} else if (strcmp (key, "LoginFrequency") == 0) {
user->login_frequency = g_value_get_uint64 (value);
} else if (strcmp (key, "IconFile") == 0) {
gboolean res;
g_free (user->icon_file);
user->icon_file = g_value_dup_string (value);
res = check_user_file (user->icon_file);
if (!res) {
g_free (user->icon_file);
user->icon_file = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
}
} else if (strcmp (key, "Locked") == 0) {
/* ignore */
} else if (strcmp (key, "AutomaticLogin") == 0) {
/* ignore */
} else if (strcmp (key, "PasswordMode") == 0) {
/* ignore */
} else if (strcmp (key, "PasswordHint") == 0) {
/* ignore */
} else if (strcmp (key, "HomeDirectory") == 0) {
/* ignore */
} else if (strcmp (key, "Shell") == 0) {
/* ignore */
} else {
handled = FALSE;
}
if (!handled) {
g_debug ("unhandled property %s", key);
}
}
static void
on_get_all_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUser *user)
{
GError *error;
GHashTable *hash_table;
gboolean res;
g_assert (user->get_all_call == call);
g_assert (user->object_proxy == proxy);
error = NULL;
res = dbus_g_proxy_end_call (proxy,
call,
&error,
dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
&hash_table,
G_TYPE_INVALID);
user->get_all_call = NULL;
user->object_proxy = NULL;
if (! res) {
g_debug ("Error calling GetAll() when retrieving properties for %s: %s",
user->object_path, error->message);
g_error_free (error);
goto out;
}
g_hash_table_foreach (hash_table, (GHFunc) collect_props, user);
g_hash_table_unref (hash_table);
if (!user->is_loaded) {
set_is_loaded (user, TRUE);
}
g_signal_emit (user, signals[CHANGED], 0);
out:
g_object_unref (proxy);
}
static gboolean
update_info (GdmUser *user)
{
DBusGProxy *proxy;
DBusGProxyCall *call;
proxy = dbus_g_proxy_new_for_name (user->connection,
ACCOUNTS_NAME,
user->object_path,
DBUS_INTERFACE_PROPERTIES);
call = dbus_g_proxy_begin_call (proxy,
"GetAll",
(DBusGProxyCallNotify)
on_get_all_finished,
user,
NULL,
G_TYPE_STRING,
ACCOUNTS_USER_INTERFACE,
G_TYPE_INVALID);
if (call == NULL) {
g_warning ("GdmUser: failed to make GetAll call");
goto failed;
}
user->get_all_call = call;
user->object_proxy = proxy;
return TRUE;
failed:
if (proxy != NULL) {
g_object_unref (proxy);
}
return FALSE;
}
static void
changed_handler (DBusGProxy *proxy,
gpointer *data)
{
GdmUser *user = GDM_USER (data);
update_info (user);
}
/**
* _gdm_user_update_from_object_path:
* @user: the user object to update.
* @object_path: the object path of the user to use.
*
* Updates the properties of @user from the accounts service via
* the object path in @object_path.
**/
void
_gdm_user_update_from_object_path (GdmUser *user,
const char *object_path)
{
g_return_if_fail (GDM_IS_USER (user));
g_return_if_fail (object_path != NULL);
g_return_if_fail (user->object_path == NULL);
user->object_path = g_strdup (object_path);
user->accounts_proxy = dbus_g_proxy_new_for_name (user->connection,
ACCOUNTS_NAME,
user->object_path,
ACCOUNTS_USER_INTERFACE);
dbus_g_proxy_set_default_timeout (user->accounts_proxy, INT_MAX);
dbus_g_proxy_add_signal (user->accounts_proxy, "Changed", G_TYPE_INVALID);
dbus_g_proxy_connect_signal (user->accounts_proxy, "Changed",
G_CALLBACK (changed_handler), user, NULL);
if (!update_info (user)) {
g_warning ("Couldn't update info for user with object path %s", object_path);
}
}
/**
* gdm_user_is_loaded:
* @user: a #GdmUser
*
* Determines whether or not the user object is loaded and ready to read from.
* #GdmUserManager:is-loaded property must be %TRUE before calling
* gdm_user_manager_list_users()
*
* Returns: %TRUE or %FALSE
*/
gboolean
gdm_user_is_loaded (GdmUser *user)
{
return user->is_loaded;
}