]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/user.c
Merged fix-tests-when-building-out-of-tree branch
[sojka/lightdm.git] / liblightdm-gobject / user.c
1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; either version 3 of the License, or (at your option) any
8  * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
9  * license.
10  */
11
12 #include <config.h>
13
14 #include <errno.h>
15 #include <string.h>
16 #include <sys/utsname.h>
17 #include <pwd.h>
18 #include <gio/gio.h>
19
20 #include "lightdm/user.h"
21
22 enum
23 {
24     LIST_PROP_0,
25     LIST_PROP_NUM_USERS,
26     LIST_PROP_USERS,
27 };
28
29 enum
30 {
31     USER_PROP_0,
32     USER_PROP_NAME,
33     USER_PROP_REAL_NAME,
34     USER_PROP_DISPLAY_NAME,
35     USER_PROP_HOME_DIRECTORY,
36     USER_PROP_IMAGE,
37     USER_PROP_BACKGROUND,
38     USER_PROP_LANGUAGE,
39     USER_PROP_LAYOUT,
40     USER_PROP_SESSION,
41     USER_PROP_LOGGED_IN
42 };
43
44 enum
45 {
46     USER_ADDED,
47     USER_CHANGED,
48     USER_REMOVED,
49     LAST_LIST_SIGNAL
50 };
51 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
52
53 enum
54 {
55     CHANGED,
56     LAST_USER_SIGNAL
57 };
58 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
59
60 typedef struct
61 {
62     /* Connection to AccountsService */
63     GDBusProxy *accounts_service_proxy;
64     GList *user_account_objects;
65
66     /* Connection to DisplayManager */
67     GDBusProxy *display_manager_proxy;
68
69     /* File monitor for password file */
70     GFileMonitor *passwd_monitor;
71   
72     /* TRUE if have scanned users */
73     gboolean have_users;
74
75     /* List of users */
76     GList *users;
77
78     /* List of sessions */
79     GList *sessions;
80 } LightDMUserListPrivate;
81
82 typedef struct
83 {
84     GDBusProxy *proxy;
85     LightDMUser *user;
86 } UserAccountObject;
87
88 typedef struct
89 {
90     LightDMUserList *user_list;
91
92     gchar *name;
93     gchar *real_name;
94     gchar *home_directory;
95     gchar *image;
96     gchar *background;
97
98     GKeyFile *dmrc_file;
99     gchar *language;
100     gchar *layout;
101     gchar *session;
102 } LightDMUserPrivate;
103
104 typedef struct
105 {
106     GObject parent_instance;
107     gchar *path;
108     gchar *username;
109 } Session;
110
111 typedef struct
112 {
113     GObjectClass parent_class;
114 } SessionClass;
115
116 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
117 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
118 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
119 GType session_get_type (void);
120 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
121
122 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
123 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
124
125 #define PASSWD_FILE      "/etc/passwd"
126 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
127
128 static LightDMUserList *singleton = NULL;
129
130 /**
131  * lightdm_user_list_get_instance:
132  *
133  * Get the user list.
134  *
135  * Return value: (tranfer none): the #LightDMUserList
136  **/
137 LightDMUserList *
138 lightdm_user_list_get_instance (void)
139 {
140     if (!singleton)
141         singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
142     return singleton;
143 }
144
145 static LightDMUser *
146 get_user_by_name (LightDMUserList *user_list, const gchar *username)
147 {
148     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
149     GList *link;
150   
151     for (link = priv->users; link; link = link->next)
152     {
153         LightDMUser *user = link->data;
154         if (strcmp (lightdm_user_get_name (user), username) == 0)
155             return user;
156     }
157
158     return NULL;
159 }
160   
161 static gint
162 compare_user (gconstpointer a, gconstpointer b)
163 {
164     LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
165     return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
166 }
167
168 static gboolean
169 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
170 {
171     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
172
173     if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
174         g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
175         g_strcmp0 (lightdm_user_get_image (user), image) == 0)
176         return FALSE;
177
178     g_free (priv->real_name);
179     priv->real_name = g_strdup (real_name);
180     g_free (priv->home_directory);
181     priv->home_directory = g_strdup (home_directory);
182     g_free (priv->image);
183     priv->image = g_strdup (image);
184
185     return TRUE;
186 }
187
188 static void
189 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
190 {
191     g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
192 }
193
194 static void
195 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
196 {
197     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
198     GKeyFile *config;
199     gchar *value;
200     gint minimum_uid;
201     gchar **hidden_users, **hidden_shells;
202     GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
203     GError *error = NULL;
204
205     g_debug ("Loading user config from %s", USER_CONFIG_FILE);
206
207     config = g_key_file_new ();
208     g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
209     if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
210         g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
211     g_clear_error (&error);
212
213     if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
214         minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
215     else
216         minimum_uid = 500;
217
218     value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
219     if (!value)
220         value = g_strdup ("nobody nobody4 noaccess");
221     hidden_users = g_strsplit (value, " ", -1);
222     g_free (value);
223
224     value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
225     if (!value)
226         value = g_strdup ("/bin/false /usr/sbin/nologin");
227     hidden_shells = g_strsplit (value, " ", -1);
228     g_free (value);
229
230     g_key_file_free (config);
231
232     setpwent ();
233
234     while (TRUE)
235     {
236         struct passwd *entry;
237         LightDMUser *user;
238         LightDMUserPrivate *user_priv;
239         char **tokens;
240         gchar *real_name, *image;
241         int i;
242
243         errno = 0;
244         entry = getpwent ();
245         if (!entry)
246             break;
247
248         /* Ignore system users */
249         if (entry->pw_uid < minimum_uid)
250             continue;
251
252         /* Ignore users disabled by shell */
253         if (entry->pw_shell)
254         {
255             for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
256             if (hidden_shells[i])
257                 continue;
258         }
259
260         /* Ignore certain users */
261         for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
262         if (hidden_users[i])
263             continue;
264
265         tokens = g_strsplit (entry->pw_gecos, ",", -1);
266         if (tokens[0] != NULL && tokens[0][0] != '\0')
267             real_name = g_strdup (tokens[0]);
268         else
269             real_name = g_strdup ("");
270         g_strfreev (tokens);
271
272         image = g_build_filename (entry->pw_dir, ".face", NULL);
273         if (!g_file_test (image, G_FILE_TEST_EXISTS))
274         {
275             g_free (image);
276             image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
277             if (!g_file_test (image, G_FILE_TEST_EXISTS))
278             {
279                 g_free (image);
280                 image = NULL;
281             }
282         }
283
284         user = g_object_new (LIGHTDM_TYPE_USER, NULL);
285         user_priv = GET_USER_PRIVATE (user);
286         user_priv->user_list = user_list;
287         g_free (user_priv->name);
288         user_priv->name = g_strdup (entry->pw_name);
289         g_free (user_priv->real_name);
290         user_priv->real_name = real_name;
291         g_free (user_priv->home_directory);
292         user_priv->home_directory = g_strdup (entry->pw_dir);
293         g_free (user_priv->image);
294         user_priv->image = image;
295
296         /* Update existing users if have them */
297         for (link = priv->users; link; link = link->next)
298         {
299             LightDMUser *info = link->data;
300             if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
301             {
302                 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
303                     changed_users = g_list_insert_sorted (changed_users, info, compare_user);
304                 g_object_unref (user);
305                 user = info;
306                 break;
307             }
308         }
309         if (!link)
310         {
311             /* Only notify once we have loaded the user list */
312             if (priv->have_users)
313                 new_users = g_list_insert_sorted (new_users, user, compare_user);
314         }
315         users = g_list_insert_sorted (users, user, compare_user);
316     }
317     g_strfreev (hidden_users);
318     g_strfreev (hidden_shells);
319
320     if (errno != 0)
321         g_warning ("Failed to read password database: %s", strerror (errno));
322
323     endpwent ();
324
325     /* Use new user list */
326     old_users = priv->users;
327     priv->users = users;
328   
329     /* Notify of changes */
330     for (link = new_users; link; link = link->next)
331     {
332         LightDMUser *info = link->data;
333         g_debug ("User %s added", lightdm_user_get_name (info));
334         g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
335         if (emit_add_signal)
336             g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
337     }
338     g_list_free (new_users);
339     for (link = changed_users; link; link = link->next)
340     {
341         LightDMUser *info = link->data;
342         g_debug ("User %s changed", lightdm_user_get_name (info));
343         g_signal_emit (info, user_signals[CHANGED], 0);
344     }
345     g_list_free (changed_users);
346     for (link = old_users; link; link = link->next)
347     {
348         GList *new_link;
349
350         /* See if this user is in the current list */
351         for (new_link = priv->users; new_link; new_link = new_link->next)
352         {
353             if (new_link->data == link->data)
354                 break;
355         }
356
357         if (!new_link)
358         {
359             LightDMUser *info = link->data;
360             g_debug ("User %s removed", lightdm_user_get_name (info));
361             g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
362             g_object_unref (info);
363         }
364     }
365     g_list_free (old_users);
366 }
367
368 static void
369 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
370 {
371     if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
372     {
373         g_debug ("%s changed, reloading user list", g_file_get_path (file));
374         load_passwd_file (user_list, TRUE);
375     }
376 }
377
378 static gboolean
379 update_user (UserAccountObject *object)
380 {
381     LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
382     GVariant *result, *value;
383     GVariantIter *iter;
384     gchar *name;
385     GError *error = NULL;
386
387     result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
388                                           "org.freedesktop.Accounts",
389                                           g_dbus_proxy_get_object_path (object->proxy),
390                                           "org.freedesktop.DBus.Properties",
391                                           "GetAll",
392                                           g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
393                                           G_VARIANT_TYPE ("(a{sv})"),
394                                           G_DBUS_CALL_FLAGS_NONE,
395                                           -1,
396                                           NULL,
397                                           &error);
398     if (error)
399         g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
400     g_clear_error (&error);
401     if (!result)
402         return FALSE;
403
404     g_variant_get (result, "(a{sv})", &iter);
405     while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
406     {
407         if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
408         {
409             gchar *user_name;
410             g_variant_get (value, "&s", &user_name);
411             g_free (priv->name);
412             priv->name = g_strdup (user_name);
413         }
414         else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
415         {
416             gchar *real_name;
417             g_variant_get (value, "&s", &real_name);
418             g_free (priv->real_name);
419             priv->real_name = g_strdup (real_name);
420         }
421         else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
422         {
423             gchar *home_directory;
424             g_variant_get (value, "&s", &home_directory);
425             g_free (priv->home_directory);
426             priv->home_directory = g_strdup (home_directory);
427         }
428         else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
429         {
430             gchar *icon_file;
431             g_variant_get (value, "&s", &icon_file);
432             g_free (priv->image);
433             if (strcmp (icon_file, "") == 0)
434                 priv->image = NULL;
435             else
436                 priv->image = g_strdup (icon_file);
437         }
438         else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
439         {
440             gchar *background_file;
441             g_variant_get (value, "&s", &background_file);
442             g_free (priv->background);
443             if (strcmp (background_file, "") == 0)
444                 priv->background = NULL;
445             else
446                 priv->background = g_strdup (background_file);
447         }
448     }
449     g_variant_iter_free (iter);
450
451     g_variant_unref (result);
452
453     return TRUE;
454 }
455
456 static void
457 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
458 {
459     if (strcmp (signal_name, "Changed") == 0)
460     {
461         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
462         {
463             g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
464             update_user (object);
465             g_signal_emit (object->user, user_signals[CHANGED], 0);
466         }
467         else
468             g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
469     }
470 }
471
472 static UserAccountObject *
473 user_account_object_new (LightDMUserList *user_list, const gchar *path)
474 {
475     GDBusProxy *proxy;
476     UserAccountObject *object;
477     GError *error = NULL;
478
479     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
480                                            G_DBUS_PROXY_FLAGS_NONE,
481                                            NULL,
482                                            "org.freedesktop.Accounts",
483                                            path,
484                                            "org.freedesktop.Accounts.User",
485                                            NULL,
486                                            &error);
487     if (error)
488         g_warning ("Error getting user %s: %s", path, error->message);
489     g_clear_error (&error);
490     if (!proxy)
491         return NULL;
492
493     object = g_malloc0 (sizeof (UserAccountObject));  
494     object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
495     GET_USER_PRIVATE (object->user)->user_list = user_list;
496     object->proxy = proxy;
497     g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
498   
499     return object;
500 }
501
502 static void
503 user_account_object_free (UserAccountObject *object)
504 {
505     if (!object)
506         return;
507     g_object_unref (object->user);
508     g_object_unref (object->proxy);
509     g_free (object);
510 }
511
512 static UserAccountObject *
513 find_user_account_object (LightDMUserList *user_list, const gchar *path)
514 {
515     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
516     GList *link;
517
518     for (link = priv->user_account_objects; link; link = link->next)
519     {
520         UserAccountObject *object = link->data;
521         if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
522             return object;
523     }
524
525     return NULL;
526 }
527
528 static void
529 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
530 {
531     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
532   
533     if (strcmp (signal_name, "UserAdded") == 0)
534     {
535         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
536         {
537             gchar *path;
538             UserAccountObject *object;
539
540             g_variant_get (parameters, "(&o)", &path);
541
542             /* Ignore duplicate requests */
543             object = find_user_account_object (user_list, path);
544             if (object)
545                 return;
546
547             object = user_account_object_new (user_list, path);
548             if (object && update_user (object))
549             {
550                 g_debug ("User %s added", path);
551                 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
552                 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
553                 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
554                 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
555             }
556             else
557                 user_account_object_free (object);
558         }
559         else
560             g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
561     }
562     else if (strcmp (signal_name, "UserDeleted") == 0)
563     {
564         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
565         {
566             gchar *path;
567             UserAccountObject *object;
568
569             g_variant_get (parameters, "(&o)", &path);
570
571             object = find_user_account_object (user_list, path);
572             if (!object)
573                 return;
574
575             g_debug ("User %s deleted", path);
576             priv->users = g_list_remove (priv->users, object->user);
577             g_object_unref (object->user);
578
579             g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
580
581             priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
582             user_account_object_free (object);
583         }
584         else
585             g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
586     }
587 }
588
589 static Session *
590 load_session (LightDMUserList *user_list, const gchar *path)
591 {
592     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
593     Session *session = NULL;
594     GVariant *result, *username;
595     GError *error = NULL;
596
597     result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
598                                           "org.freedesktop.DisplayManager",
599                                           path,
600                                           "org.freedesktop.DBus.Properties",
601                                           "Get",
602                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
603                                           G_VARIANT_TYPE ("(v)"),
604                                           G_DBUS_CALL_FLAGS_NONE,
605                                           -1,
606                                           NULL,
607                                           &error);
608     if (error)
609         g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
610     g_clear_error (&error);
611     if (!result)
612         return NULL;
613
614     g_variant_get (result, "(v)", &username);
615     if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
616     {
617         gchar *name;
618
619         g_variant_get (username, "&s", &name);
620
621         g_debug ("Loaded session %s (%s)", path, name);
622         session = g_object_new (session_get_type (), NULL);
623         session->username = g_strdup (name);
624         session->path = g_strdup (path);
625         priv->sessions = g_list_append (priv->sessions, session);
626     }
627     g_variant_unref (username);
628     g_variant_unref (result);
629
630     return session;
631 }
632
633 static void
634 display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
635 {
636     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
637
638     if (strcmp (signal_name, "SessionAdded") == 0)
639     {
640         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
641         {
642             gchar *path;
643             Session *session;
644             LightDMUser *user = NULL;
645
646             g_variant_get (parameters, "(&o)", &path);
647             session = load_session (user_list, path);
648             if (session)
649                 user = get_user_by_name (user_list, session->username);
650             if (user)
651                 g_signal_emit (user, user_signals[CHANGED], 0);
652         }
653     }
654     else if (strcmp (signal_name, "SessionRemoved") == 0)
655     {
656         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
657         {
658             gchar *path;
659             GList *link;
660
661             g_variant_get (parameters, "(&o)", &path);
662
663             for (link = priv->sessions; link; link = link->next)
664             {
665                 Session *session = link->data;
666                 if (strcmp (session->path, path) == 0)
667                 {
668                     LightDMUser *user;
669
670                     g_debug ("Session %s removed", path);
671                     priv->sessions = g_list_remove_link (priv->sessions, link);
672                     user = get_user_by_name (user_list, session->username);
673                     if (user)
674                         g_signal_emit (user, user_signals[CHANGED], 0);
675                     g_object_unref (session);
676                     break;
677                 }
678             }
679         }
680     }
681 }
682
683 static void
684 update_users (LightDMUserList *user_list)
685 {
686     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
687     GError *error = NULL;
688
689     if (priv->have_users)
690         return;
691     priv->have_users = TRUE;
692
693     priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
694                                                                   G_DBUS_PROXY_FLAGS_NONE,
695                                                                   NULL,
696                                                                   "org.freedesktop.Accounts",
697                                                                   "/org/freedesktop/Accounts",
698                                                                   "org.freedesktop.Accounts",
699                                                                   NULL,
700                                                                   &error);
701     if (error)
702         g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
703     g_clear_error (&error);
704
705     /* Check if the service exists */
706     if (priv->accounts_service_proxy)
707     {
708         gchar *name;
709
710         name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
711         if (!name)
712         {
713             g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
714             g_object_unref (priv->accounts_service_proxy);
715             priv->accounts_service_proxy = NULL;
716         }
717         g_free (name);
718     }
719
720     if (priv->accounts_service_proxy)
721     {
722         GVariant *result;
723
724         g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
725
726         result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
727                                          "ListCachedUsers",
728                                          g_variant_new ("()"),
729                                          G_DBUS_CALL_FLAGS_NONE,
730                                          -1,
731                                          NULL,
732                                          &error);
733         if (error)
734             g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
735         g_clear_error (&error);
736         if (!result)
737             return;
738
739         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
740         {
741             GVariantIter *iter;
742             const gchar *path;
743
744             g_debug ("Loading users from org.freedesktop.Accounts");
745             g_variant_get (result, "(ao)", &iter);
746             while (g_variant_iter_loop (iter, "&o", &path))
747             {
748                 UserAccountObject *object;
749
750                 g_debug ("Loading user %s", path);
751
752                 object = user_account_object_new (user_list, path);
753                 if (object && update_user (object))
754                 {
755                     priv->user_account_objects = g_list_append (priv->user_account_objects, object);
756                     priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
757                     g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
758                 }
759                 else
760                     user_account_object_free (object);
761             }
762             g_variant_iter_free (iter);
763         }
764         else
765             g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
766
767         g_variant_unref (result);
768     }
769     else
770     {
771         GFile *passwd_file;
772
773         load_passwd_file (user_list, FALSE);
774
775         /* Watch for changes to user list */
776         passwd_file = g_file_new_for_path (PASSWD_FILE);
777         priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
778         g_object_unref (passwd_file);
779         if (error)
780             g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
781         else
782             g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
783         g_clear_error (&error);
784     }
785
786     priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
787                                                                  G_DBUS_PROXY_FLAGS_NONE,
788                                                                  NULL,
789                                                                  "org.freedesktop.DisplayManager",
790                                                                  "/org/freedesktop/DisplayManager",
791                                                                  "org.freedesktop.DisplayManager",
792                                                                  NULL,
793                                                                  &error);
794     if (error)
795         g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
796     g_clear_error (&error);
797
798     if (priv->display_manager_proxy)
799     {
800         GVariant *result;
801
802         g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
803
804         result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
805                                               "org.freedesktop.DisplayManager",
806                                               "/org/freedesktop/DisplayManager",
807                                               "org.freedesktop.DBus.Properties",
808                                               "Get",
809                                               g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
810                                               G_VARIANT_TYPE ("(v)"),
811                                               G_DBUS_CALL_FLAGS_NONE,
812                                               -1,
813                                               NULL,
814                                               &error);
815         if (error)
816             g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
817         g_clear_error (&error);
818         if (!result)
819             return;
820
821         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
822         {
823             GVariant *value;
824             GVariantIter *iter;
825             const gchar *path;
826
827             g_variant_get (result, "(v)", &value);
828
829             g_debug ("Loading sessions from org.freedesktop.DisplayManager");
830             g_variant_get (value, "ao", &iter);
831             while (g_variant_iter_loop (iter, "&o", &path))
832                 load_session (user_list, path);
833             g_variant_iter_free (iter);
834
835             g_variant_unref (value);
836         }
837         else
838             g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
839
840         g_variant_unref (result);
841     }
842 }
843
844 /**
845  * lightdm_user_list_get_length:
846  * @user_list: a #LightDMUserList
847  *
848  * Return value: The number of users able to log in
849  **/
850 gint
851 lightdm_user_list_get_length (LightDMUserList *user_list)
852 {
853     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
854     update_users (user_list);
855     return g_list_length (GET_LIST_PRIVATE (user_list)->users);
856 }
857
858 /**
859  * lightdm_user_list_get_users:
860  * @user_list: A #LightDMUserList
861  *
862  * Get a list of users to present to the user.  This list may be a subset of the
863  * available users and may be empty depending on the server configuration.
864  *
865  * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
866  **/
867 GList *
868 lightdm_user_list_get_users (LightDMUserList *user_list)
869 {
870     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
871     update_users (user_list);
872     return GET_LIST_PRIVATE (user_list)->users;
873 }
874
875 /**
876  * lightdm_user_list_get_user_by_name:
877  * @user_list: A #LightDMUserList
878  * @username: Name of user to get.
879  *
880  * Get infomation about a given user or #NULL if this user doesn't exist.
881  *
882  * Return value: (transfer none): A #LightDMUser entry for the given user.
883  **/
884 LightDMUser *
885 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
886 {
887     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
888     g_return_val_if_fail (username != NULL, NULL);
889
890     update_users (user_list);
891
892     return get_user_by_name (user_list, username);
893 }
894
895 static void
896 lightdm_user_list_init (LightDMUserList *user_list)
897 {
898 }
899
900 static void
901 lightdm_user_list_set_property (GObject    *object,
902                                 guint       prop_id,
903                                 const GValue *value,
904                                 GParamSpec *pspec)
905 {
906     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
907 }
908
909 static void
910 lightdm_user_list_get_property (GObject    *object,
911                                 guint       prop_id,
912                                 GValue     *value,
913                                 GParamSpec *pspec)
914 {
915     LightDMUserList *self;
916
917     self = LIGHTDM_USER_LIST (object);
918
919     switch (prop_id)
920     {
921     case LIST_PROP_NUM_USERS:
922         g_value_set_int (value, lightdm_user_list_get_length (self));
923         break;
924     default:
925         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
926         break;
927     }
928 }
929
930 static void
931 lightdm_user_list_finalize (GObject *object)
932 {
933     LightDMUserList *self = LIGHTDM_USER_LIST (object);
934     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
935
936     if (priv->accounts_service_proxy)
937         g_object_unref (priv->accounts_service_proxy);
938     g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
939     if (priv->passwd_monitor)
940         g_object_unref (priv->passwd_monitor);
941     g_list_free_full (priv->users, g_object_unref);
942     g_list_free_full (priv->sessions, g_object_unref);
943
944     G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
945 }
946
947 static void
948 lightdm_user_list_class_init (LightDMUserListClass *klass)
949 {
950     GObjectClass *object_class = G_OBJECT_CLASS (klass);
951
952     g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
953
954     object_class->set_property = lightdm_user_list_set_property;
955     object_class->get_property = lightdm_user_list_get_property;
956     object_class->finalize = lightdm_user_list_finalize;
957
958     g_object_class_install_property (object_class,
959                                      LIST_PROP_NUM_USERS,
960                                      g_param_spec_int ("num-users",
961                                                        "num-users",
962                                                        "Number of login users",
963                                                        0, G_MAXINT, 0,
964                                                        G_PARAM_READABLE));
965     /**
966      * LightDMUserList::user-added:
967      * @user_list: A #LightDMUserList
968      * @user: The #LightDM user that has been added.
969      *
970      * The ::user-added signal gets emitted when a user account is created.
971      **/
972     list_signals[USER_ADDED] =
973         g_signal_new ("user-added",
974                       G_TYPE_FROM_CLASS (klass),
975                       G_SIGNAL_RUN_LAST,
976                       G_STRUCT_OFFSET (LightDMUserListClass, user_added),
977                       NULL, NULL,
978                       g_cclosure_marshal_VOID__OBJECT,
979                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
980
981     /**
982      * LightDMUserList::user-changed:
983      * @user_list: A #LightDMUserList
984      * @user: The #LightDM user that has been changed.
985      *
986      * The ::user-changed signal gets emitted when a user account is modified.
987      **/
988     list_signals[USER_CHANGED] =
989         g_signal_new ("user-changed",
990                       G_TYPE_FROM_CLASS (klass),
991                       G_SIGNAL_RUN_LAST,
992                       G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
993                       NULL, NULL,
994                       g_cclosure_marshal_VOID__OBJECT,
995                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
996
997     /**
998      * LightDMUserList::user-removed:
999      * @user_list: A #LightDMUserList
1000      * @user: The #LightDM user that has been removed.
1001      *
1002      * The ::user-removed signal gets emitted when a user account is removed.
1003      **/
1004     list_signals[USER_REMOVED] =
1005         g_signal_new ("user-removed",
1006                       G_TYPE_FROM_CLASS (klass),
1007                       G_SIGNAL_RUN_LAST,
1008                       G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1009                       NULL, NULL,
1010                       g_cclosure_marshal_VOID__OBJECT,
1011                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1012 }
1013
1014 /**
1015  * lightdm_user_get_name:
1016  * @user: A #LightDMUser
1017  * 
1018  * Get the name of a user.
1019  * 
1020  * Return value: The name of the given user
1021  **/
1022 const gchar *
1023 lightdm_user_get_name (LightDMUser *user)
1024 {
1025     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1026     return GET_USER_PRIVATE (user)->name;
1027 }
1028
1029 /**
1030  * lightdm_user_get_real_name:
1031  * @user: A #LightDMUser
1032  * 
1033  * Get the real name of a user.
1034  *
1035  * Return value: The real name of the given user
1036  **/
1037 const gchar *
1038 lightdm_user_get_real_name (LightDMUser *user)
1039 {
1040     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1041     return GET_USER_PRIVATE (user)->real_name;
1042 }
1043
1044 /**
1045  * lightdm_user_get_display_name:
1046  * @user: A #LightDMUser
1047  * 
1048  * Get the display name of a user.
1049  * 
1050  * Return value: The display name of the given user
1051  **/
1052 const gchar *
1053 lightdm_user_get_display_name (LightDMUser *user)
1054 {
1055     LightDMUserPrivate *priv;
1056
1057     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1058
1059     priv = GET_USER_PRIVATE (user);
1060     if (strcmp (priv->real_name, ""))
1061         return priv->real_name;
1062     else
1063         return priv->name;
1064 }
1065
1066 /**
1067  * lightdm_user_get_home_directory:
1068  * @user: A #LightDMUser
1069  * 
1070  * Get the home directory for a user.
1071  * 
1072  * Return value: The users home directory
1073  */
1074 const gchar *
1075 lightdm_user_get_home_directory (LightDMUser *user)
1076 {
1077     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1078     return GET_USER_PRIVATE (user)->home_directory;
1079 }
1080
1081 /**
1082  * lightdm_user_get_image:
1083  * @user: A #LightDMUser
1084  * 
1085  * Get the image URI for a user.
1086  * 
1087  * Return value: The image URI for the given user or #NULL if no URI
1088  **/
1089 const gchar *
1090 lightdm_user_get_image (LightDMUser *user)
1091 {
1092     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1093     return GET_USER_PRIVATE (user)->image;
1094 }
1095
1096 /**
1097  * lightdm_user_get_background:
1098  * @user: A #LightDMUser
1099  * 
1100  * Get the background file path for a user.
1101  * 
1102  * Return value: The background file path for the given user or #NULL if no path
1103  **/
1104 const gchar *
1105 lightdm_user_get_background (LightDMUser *user)
1106 {
1107     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1108     return GET_USER_PRIVATE (user)->background;
1109 }
1110
1111 static void
1112 load_dmrc (LightDMUser *user)
1113 {
1114     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1115     gchar *path;
1116     //gboolean have_dmrc;
1117
1118     priv->dmrc_file = g_key_file_new ();
1119
1120     /* Load from the user directory */  
1121     path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1122     /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1123     g_free (path);
1124
1125     /* If no ~/.dmrc, then load from the cache */
1126     // FIXME
1127
1128     // FIXME: Watch for changes
1129
1130     if (priv->language)
1131         g_free (priv->language);
1132     if (priv->layout)
1133         g_free (priv->layout);
1134     if (priv->session)
1135         g_free (priv->session);
1136
1137     /* The Language field is actually a locale, strip the codeset off it to get the language */
1138     priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1139     if (priv->language)
1140     {
1141         gchar *codeset = strchr (priv->language, '.');
1142         if (codeset)
1143             *codeset = '\0';
1144     }
1145
1146     priv->layout = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1147     priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1148 }
1149
1150 static gchar *
1151 get_string_property (GDBusProxy *proxy, const gchar *property)
1152 {
1153     GVariant *answer;
1154     gchar *rv;
1155
1156     if (!proxy)
1157         return NULL;
1158
1159     answer = g_dbus_proxy_get_cached_property (proxy, property);
1160
1161     if (!answer)
1162     {
1163         g_warning ("Could not get accounts property %s", property);
1164         return NULL;
1165     }
1166
1167     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("s")))
1168     {
1169         g_warning ("Unexpected accounts property type for %s: %s",
1170                    property, g_variant_get_type_string (answer));
1171         g_variant_unref (answer);
1172         return NULL;
1173     }
1174
1175     g_variant_get (answer, "s", &rv);
1176
1177     g_variant_unref (answer);
1178     return rv;
1179 }
1180
1181 static gboolean
1182 load_accounts_service (LightDMUser *user)
1183 {
1184     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1185     LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1186
1187     /* First, find AccountObject proxy */
1188     UserAccountObject *account = NULL;
1189     GList *iter;
1190     for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1191     {
1192         if (((UserAccountObject *)iter->data)->user == user)
1193         {
1194             account = (UserAccountObject *)iter->data;
1195             break;
1196         }
1197     }
1198     if (!account)
1199         return FALSE;
1200
1201     /* We have proxy, let's grab some properties */
1202     if (priv->language)
1203         g_free (priv->language);
1204     if (priv->session)
1205         g_free (priv->session);
1206     priv->language = get_string_property (account->proxy, "Language");
1207     priv->session = get_string_property (account->proxy, "XSession");
1208
1209     return TRUE;
1210 }
1211
1212 /* Loads language/layout/session info for user */
1213 static void
1214 load_user_values (LightDMUser *user)
1215 {
1216     load_dmrc (user);
1217     load_accounts_service (user); // overrides dmrc values
1218 }
1219
1220 /**
1221  * lightdm_user_get_language
1222  * @user: A #LightDMUser
1223  * 
1224  * Get the language for a user.
1225  * 
1226  * Return value: The language for the given user or #NULL if using system defaults.
1227  **/
1228 const gchar *
1229 lightdm_user_get_language (LightDMUser *user)
1230 {
1231     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1232     load_user_values (user);
1233     return GET_USER_PRIVATE (user)->language;
1234 }
1235
1236 /**
1237  * lightdm_user_get_layout
1238  * @user: A #LightDMUser
1239  * 
1240  * Get the keyboard layout for a user.
1241  * 
1242  * Return value: The keyboard layoyt for the given user or #NULL if using system defaults.
1243  **/
1244 const gchar *
1245 lightdm_user_get_layout (LightDMUser *user)
1246 {
1247     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1248     load_user_values (user);
1249     return GET_USER_PRIVATE (user)->layout;
1250 }
1251
1252 /**
1253  * lightdm_user_get_session
1254  * @user: A #LightDMUser
1255  * 
1256  * Get the session for a user.
1257  * 
1258  * Return value: The session for the given user or #NULL if using system defaults.
1259  **/
1260 const gchar *
1261 lightdm_user_get_session (LightDMUser *user)
1262 {
1263     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1264     load_user_values (user);
1265     return GET_USER_PRIVATE (user)->session; 
1266 }
1267
1268 /**
1269  * lightdm_user_get_logged_in:
1270  * @user: A #LightDMUser
1271  * 
1272  * Check if a user is logged in.
1273  * 
1274  * Return value: #TRUE if the user is currently logged in.
1275  **/
1276 gboolean
1277 lightdm_user_get_logged_in (LightDMUser *user)
1278 {
1279     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1280     LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1281     GList *link;
1282
1283     g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1284
1285     for (link = list_priv->sessions; link; link = link->next)
1286     {
1287         Session *session = link->data;
1288         if (strcmp (session->username, priv->name) == 0)
1289             return TRUE;
1290     }
1291
1292     return FALSE;
1293 }
1294
1295 static void
1296 lightdm_user_init (LightDMUser *user)
1297 {
1298     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1299
1300     priv->name = g_strdup ("");
1301     priv->real_name = g_strdup ("");
1302     priv->home_directory = g_strdup ("");
1303     priv->image = g_strdup ("");
1304     priv->background = g_strdup ("");
1305     priv->language = g_strdup ("");
1306     priv->layout = g_strdup ("");
1307     priv->session = g_strdup ("");
1308 }
1309
1310 static void
1311 lightdm_user_set_property (GObject    *object,
1312                            guint       prop_id,
1313                            const GValue *value,
1314                            GParamSpec *pspec)
1315 {
1316     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1317 }
1318
1319 static void
1320 lightdm_user_get_property (GObject    *object,
1321                            guint       prop_id,
1322                            GValue     *value,
1323                            GParamSpec *pspec)
1324 {
1325     LightDMUser *self;
1326
1327     self = LIGHTDM_USER (object);
1328
1329     switch (prop_id)
1330     {
1331     case USER_PROP_NAME:
1332         g_value_set_string (value, lightdm_user_get_name (self));
1333         break;
1334     case USER_PROP_REAL_NAME:
1335         g_value_set_string (value, lightdm_user_get_real_name (self));
1336         break;
1337     case USER_PROP_DISPLAY_NAME:
1338         g_value_set_string (value, lightdm_user_get_display_name (self));
1339         break;
1340     case USER_PROP_HOME_DIRECTORY:
1341         g_value_set_string (value, lightdm_user_get_home_directory (self));
1342         break;
1343     case USER_PROP_IMAGE:
1344         g_value_set_string (value, lightdm_user_get_image (self));
1345         break;
1346     case USER_PROP_BACKGROUND:
1347         g_value_set_string (value, lightdm_user_get_background (self));
1348         break;
1349     case USER_PROP_LANGUAGE:
1350         g_value_set_string (value, lightdm_user_get_language (self));
1351         break;
1352     case USER_PROP_LAYOUT:
1353         g_value_set_string (value, lightdm_user_get_layout (self));
1354         break;
1355     case USER_PROP_SESSION:
1356         g_value_set_string (value, lightdm_user_get_session (self));
1357         break;
1358     case USER_PROP_LOGGED_IN:
1359         g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1360         break;
1361     default:
1362         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1363         break;
1364     }
1365 }
1366
1367 static void
1368 lightdm_user_finalize (GObject *object)
1369 {
1370     LightDMUser *self = LIGHTDM_USER (object);
1371     LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1372
1373     g_free (priv->name);
1374     g_free (priv->real_name);
1375     g_free (priv->home_directory);
1376     g_free (priv->image);
1377     g_free (priv->background);
1378     if (priv->dmrc_file)
1379         g_key_file_free (priv->dmrc_file);
1380 }
1381
1382 static void
1383 lightdm_user_class_init (LightDMUserClass *klass)
1384 {
1385     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1386   
1387     g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1388
1389     object_class->set_property = lightdm_user_set_property;
1390     object_class->get_property = lightdm_user_get_property;
1391     object_class->finalize = lightdm_user_finalize;
1392
1393     g_object_class_install_property (object_class,
1394                                      USER_PROP_NAME,
1395                                      g_param_spec_string ("name",
1396                                                           "name",
1397                                                           "Username",
1398                                                           NULL,
1399                                                           G_PARAM_READWRITE));
1400     g_object_class_install_property (object_class,
1401                                      USER_PROP_REAL_NAME,
1402                                      g_param_spec_string ("real-name",
1403                                                           "real-name",
1404                                                           "Users real name",
1405                                                           NULL,
1406                                                           G_PARAM_READWRITE));
1407     g_object_class_install_property (object_class,
1408                                      USER_PROP_DISPLAY_NAME,
1409                                      g_param_spec_string ("display-name",
1410                                                           "display-name",
1411                                                           "Users display name",
1412                                                           NULL,
1413                                                           G_PARAM_READABLE));
1414     g_object_class_install_property (object_class,
1415                                      USER_PROP_HOME_DIRECTORY,
1416                                      g_param_spec_string ("home-directory",
1417                                                           "home-directory",
1418                                                           "Home directory",
1419                                                           NULL,
1420                                                           G_PARAM_READWRITE));
1421     g_object_class_install_property (object_class,
1422                                      USER_PROP_IMAGE,
1423                                      g_param_spec_string ("image",
1424                                                           "image",
1425                                                           "Avatar image",
1426                                                           NULL,
1427                                                           G_PARAM_READWRITE));
1428     g_object_class_install_property (object_class,
1429                                      USER_PROP_BACKGROUND,
1430                                      g_param_spec_string ("background",
1431                                                           "background",
1432                                                           "User background",
1433                                                           NULL,
1434                                                           G_PARAM_READWRITE));
1435     g_object_class_install_property (object_class,
1436                                      USER_PROP_LANGUAGE,
1437                                      g_param_spec_string ("language",
1438                                                          "language",
1439                                                          "Language used by this user",
1440                                                          NULL,
1441                                                          G_PARAM_READABLE));
1442     g_object_class_install_property (object_class,
1443                                      USER_PROP_LAYOUT,
1444                                      g_param_spec_string ("layout",
1445                                                           "layout",
1446                                                           "Keyboard layout used by this user",
1447                                                           NULL,
1448                                                           G_PARAM_READABLE));
1449     g_object_class_install_property (object_class,
1450                                      USER_PROP_SESSION,
1451                                      g_param_spec_string ("session",
1452                                                           "session",
1453                                                           "Session used by this user",
1454                                                           NULL,
1455                                                           G_PARAM_READABLE));
1456     g_object_class_install_property (object_class,
1457                                      USER_PROP_LOGGED_IN,
1458                                      g_param_spec_boolean ("logged-in",
1459                                                            "logged-in",
1460                                                            "TRUE if the user is currently in a session",
1461                                                            FALSE,
1462                                                            G_PARAM_READWRITE));
1463
1464     /**
1465      * LightDMUser::changed:
1466      * @user: A #LightDMUser
1467      *
1468      * The ::changed signal gets emitted this user account is modified.
1469      **/
1470     user_signals[CHANGED] =
1471         g_signal_new ("changed",
1472                       G_TYPE_FROM_CLASS (klass),
1473                       G_SIGNAL_RUN_LAST,
1474                       G_STRUCT_OFFSET (LightDMUserClass, changed),
1475                       NULL, NULL,
1476                       g_cclosure_marshal_VOID__VOID,
1477                       G_TYPE_NONE, 0);
1478 }
1479
1480 static void
1481 session_init (Session *session)
1482 {
1483 }
1484
1485 static void
1486 session_finalize (GObject *object)
1487 {
1488     Session *self = SESSION (object);
1489
1490     g_free (self->path);
1491     g_free (self->username);
1492 }
1493
1494 static void
1495 session_class_init (SessionClass *klass)
1496 {
1497     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1498     object_class->finalize = session_finalize;
1499 }