]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/user.c
Test if language can be retrieved when accounts service not running
[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         const gchar *passwd_filename;
772         GFile *passwd_file;
773
774         load_passwd_file (user_list, FALSE);
775
776         /* Watch for changes to user list */
777
778         passwd_filename = g_getenv ("LIGHTDM_TEST_PASSWD_FILE");
779         if (!passwd_filename)
780             passwd_filename = PASSWD_FILE;
781         passwd_file = g_file_new_for_path (PASSWD_FILE);
782         priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
783         g_object_unref (passwd_file);
784         if (error)
785             g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
786         else
787             g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
788         g_clear_error (&error);
789     }
790
791     priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
792                                                                  G_DBUS_PROXY_FLAGS_NONE,
793                                                                  NULL,
794                                                                  "org.freedesktop.DisplayManager",
795                                                                  "/org/freedesktop/DisplayManager",
796                                                                  "org.freedesktop.DisplayManager",
797                                                                  NULL,
798                                                                  &error);
799     if (error)
800         g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
801     g_clear_error (&error);
802
803     if (priv->display_manager_proxy)
804     {
805         GVariant *result;
806
807         g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
808
809         result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
810                                               "org.freedesktop.DisplayManager",
811                                               "/org/freedesktop/DisplayManager",
812                                               "org.freedesktop.DBus.Properties",
813                                               "Get",
814                                               g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
815                                               G_VARIANT_TYPE ("(v)"),
816                                               G_DBUS_CALL_FLAGS_NONE,
817                                               -1,
818                                               NULL,
819                                               &error);
820         if (error)
821             g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
822         g_clear_error (&error);
823         if (!result)
824             return;
825
826         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
827         {
828             GVariant *value;
829             GVariantIter *iter;
830             const gchar *path;
831
832             g_variant_get (result, "(v)", &value);
833
834             g_debug ("Loading sessions from org.freedesktop.DisplayManager");
835             g_variant_get (value, "ao", &iter);
836             while (g_variant_iter_loop (iter, "&o", &path))
837                 load_session (user_list, path);
838             g_variant_iter_free (iter);
839
840             g_variant_unref (value);
841         }
842         else
843             g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
844
845         g_variant_unref (result);
846     }
847 }
848
849 /**
850  * lightdm_user_list_get_length:
851  * @user_list: a #LightDMUserList
852  *
853  * Return value: The number of users able to log in
854  **/
855 gint
856 lightdm_user_list_get_length (LightDMUserList *user_list)
857 {
858     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
859     update_users (user_list);
860     return g_list_length (GET_LIST_PRIVATE (user_list)->users);
861 }
862
863 /**
864  * lightdm_user_list_get_users:
865  * @user_list: A #LightDMUserList
866  *
867  * Get a list of users to present to the user.  This list may be a subset of the
868  * available users and may be empty depending on the server configuration.
869  *
870  * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
871  **/
872 GList *
873 lightdm_user_list_get_users (LightDMUserList *user_list)
874 {
875     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
876     update_users (user_list);
877     return GET_LIST_PRIVATE (user_list)->users;
878 }
879
880 /**
881  * lightdm_user_list_get_user_by_name:
882  * @user_list: A #LightDMUserList
883  * @username: Name of user to get.
884  *
885  * Get infomation about a given user or #NULL if this user doesn't exist.
886  *
887  * Return value: (transfer none): A #LightDMUser entry for the given user.
888  **/
889 LightDMUser *
890 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
891 {
892     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
893     g_return_val_if_fail (username != NULL, NULL);
894
895     update_users (user_list);
896
897     return get_user_by_name (user_list, username);
898 }
899
900 static void
901 lightdm_user_list_init (LightDMUserList *user_list)
902 {
903 }
904
905 static void
906 lightdm_user_list_set_property (GObject    *object,
907                                 guint       prop_id,
908                                 const GValue *value,
909                                 GParamSpec *pspec)
910 {
911     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
912 }
913
914 static void
915 lightdm_user_list_get_property (GObject    *object,
916                                 guint       prop_id,
917                                 GValue     *value,
918                                 GParamSpec *pspec)
919 {
920     LightDMUserList *self;
921
922     self = LIGHTDM_USER_LIST (object);
923
924     switch (prop_id)
925     {
926     case LIST_PROP_NUM_USERS:
927         g_value_set_int (value, lightdm_user_list_get_length (self));
928         break;
929     default:
930         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
931         break;
932     }
933 }
934
935 static void
936 lightdm_user_list_finalize (GObject *object)
937 {
938     LightDMUserList *self = LIGHTDM_USER_LIST (object);
939     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
940
941     if (priv->accounts_service_proxy)
942         g_object_unref (priv->accounts_service_proxy);
943     g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
944     if (priv->passwd_monitor)
945         g_object_unref (priv->passwd_monitor);
946     g_list_free_full (priv->users, g_object_unref);
947     g_list_free_full (priv->sessions, g_object_unref);
948
949     G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
950 }
951
952 static void
953 lightdm_user_list_class_init (LightDMUserListClass *klass)
954 {
955     GObjectClass *object_class = G_OBJECT_CLASS (klass);
956
957     g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
958
959     object_class->set_property = lightdm_user_list_set_property;
960     object_class->get_property = lightdm_user_list_get_property;
961     object_class->finalize = lightdm_user_list_finalize;
962
963     g_object_class_install_property (object_class,
964                                      LIST_PROP_NUM_USERS,
965                                      g_param_spec_int ("num-users",
966                                                        "num-users",
967                                                        "Number of login users",
968                                                        0, G_MAXINT, 0,
969                                                        G_PARAM_READABLE));
970     /**
971      * LightDMUserList::user-added:
972      * @user_list: A #LightDMUserList
973      * @user: The #LightDM user that has been added.
974      *
975      * The ::user-added signal gets emitted when a user account is created.
976      **/
977     list_signals[USER_ADDED] =
978         g_signal_new ("user-added",
979                       G_TYPE_FROM_CLASS (klass),
980                       G_SIGNAL_RUN_LAST,
981                       G_STRUCT_OFFSET (LightDMUserListClass, user_added),
982                       NULL, NULL,
983                       g_cclosure_marshal_VOID__OBJECT,
984                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
985
986     /**
987      * LightDMUserList::user-changed:
988      * @user_list: A #LightDMUserList
989      * @user: The #LightDM user that has been changed.
990      *
991      * The ::user-changed signal gets emitted when a user account is modified.
992      **/
993     list_signals[USER_CHANGED] =
994         g_signal_new ("user-changed",
995                       G_TYPE_FROM_CLASS (klass),
996                       G_SIGNAL_RUN_LAST,
997                       G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
998                       NULL, NULL,
999                       g_cclosure_marshal_VOID__OBJECT,
1000                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1001
1002     /**
1003      * LightDMUserList::user-removed:
1004      * @user_list: A #LightDMUserList
1005      * @user: The #LightDM user that has been removed.
1006      *
1007      * The ::user-removed signal gets emitted when a user account is removed.
1008      **/
1009     list_signals[USER_REMOVED] =
1010         g_signal_new ("user-removed",
1011                       G_TYPE_FROM_CLASS (klass),
1012                       G_SIGNAL_RUN_LAST,
1013                       G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1014                       NULL, NULL,
1015                       g_cclosure_marshal_VOID__OBJECT,
1016                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1017 }
1018
1019 /**
1020  * lightdm_user_get_name:
1021  * @user: A #LightDMUser
1022  * 
1023  * Get the name of a user.
1024  * 
1025  * Return value: The name of the given user
1026  **/
1027 const gchar *
1028 lightdm_user_get_name (LightDMUser *user)
1029 {
1030     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1031     return GET_USER_PRIVATE (user)->name;
1032 }
1033
1034 /**
1035  * lightdm_user_get_real_name:
1036  * @user: A #LightDMUser
1037  * 
1038  * Get the real name of a user.
1039  *
1040  * Return value: The real name of the given user
1041  **/
1042 const gchar *
1043 lightdm_user_get_real_name (LightDMUser *user)
1044 {
1045     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1046     return GET_USER_PRIVATE (user)->real_name;
1047 }
1048
1049 /**
1050  * lightdm_user_get_display_name:
1051  * @user: A #LightDMUser
1052  * 
1053  * Get the display name of a user.
1054  * 
1055  * Return value: The display name of the given user
1056  **/
1057 const gchar *
1058 lightdm_user_get_display_name (LightDMUser *user)
1059 {
1060     LightDMUserPrivate *priv;
1061
1062     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1063
1064     priv = GET_USER_PRIVATE (user);
1065     if (strcmp (priv->real_name, ""))
1066         return priv->real_name;
1067     else
1068         return priv->name;
1069 }
1070
1071 /**
1072  * lightdm_user_get_home_directory:
1073  * @user: A #LightDMUser
1074  * 
1075  * Get the home directory for a user.
1076  * 
1077  * Return value: The users home directory
1078  */
1079 const gchar *
1080 lightdm_user_get_home_directory (LightDMUser *user)
1081 {
1082     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1083     return GET_USER_PRIVATE (user)->home_directory;
1084 }
1085
1086 /**
1087  * lightdm_user_get_image:
1088  * @user: A #LightDMUser
1089  * 
1090  * Get the image URI for a user.
1091  * 
1092  * Return value: The image URI for the given user or #NULL if no URI
1093  **/
1094 const gchar *
1095 lightdm_user_get_image (LightDMUser *user)
1096 {
1097     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1098     return GET_USER_PRIVATE (user)->image;
1099 }
1100
1101 /**
1102  * lightdm_user_get_background:
1103  * @user: A #LightDMUser
1104  * 
1105  * Get the background file path for a user.
1106  * 
1107  * Return value: The background file path for the given user or #NULL if no path
1108  **/
1109 const gchar *
1110 lightdm_user_get_background (LightDMUser *user)
1111 {
1112     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1113     return GET_USER_PRIVATE (user)->background;
1114 }
1115
1116 static void
1117 load_dmrc (LightDMUser *user)
1118 {
1119     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1120     gchar *path;
1121     //gboolean have_dmrc;
1122
1123     priv->dmrc_file = g_key_file_new ();
1124
1125     /* Load from the user directory */  
1126     path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1127     /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1128     g_free (path);
1129
1130     /* If no ~/.dmrc, then load from the cache */
1131     // FIXME
1132
1133     // FIXME: Watch for changes
1134
1135     /* The Language field is actually a locale, strip the codeset off it to get the language */
1136     if (priv->language)
1137         g_free (priv->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     if (priv->layout)
1147         g_free (priv->layout);
1148     priv->layout = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1149
1150     if (priv->session)
1151         g_free (priv->session);
1152     priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1153 }
1154
1155 static gchar *
1156 get_string_property (GDBusProxy *proxy, const gchar *property)
1157 {
1158     GVariant *answer;
1159     gchar *rv;
1160
1161     if (!proxy)
1162         return NULL;
1163
1164     answer = g_dbus_proxy_get_cached_property (proxy, property);
1165
1166     if (!answer)
1167     {
1168         g_warning ("Could not get accounts property %s", property);
1169         return NULL;
1170     }
1171
1172     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("s")))
1173     {
1174         g_warning ("Unexpected accounts property type for %s: %s",
1175                    property, g_variant_get_type_string (answer));
1176         g_variant_unref (answer);
1177         return NULL;
1178     }
1179
1180     g_variant_get (answer, "s", &rv);
1181   
1182     if (strcmp (rv, "") == 0)
1183     {
1184         g_free (rv);
1185         rv = NULL;
1186     }
1187
1188     g_variant_unref (answer);
1189     return rv;
1190 }
1191
1192 static gboolean
1193 load_accounts_service (LightDMUser *user)
1194 {
1195     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1196     LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1197     UserAccountObject *account = NULL;
1198     GList *iter;
1199
1200     /* First, find AccountObject proxy */
1201     for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1202     {
1203         UserAccountObject *a = iter->data;
1204         if (a->user == user)
1205         {
1206             account = a;
1207             break;
1208         }
1209     }
1210     if (!account)
1211         return FALSE;
1212
1213     /* We have proxy, let's grab some properties */
1214     if (priv->language)
1215         g_free (priv->language);
1216     priv->language = get_string_property (account->proxy, "Language");
1217     if (priv->session)
1218         g_free (priv->session);
1219     priv->session = get_string_property (account->proxy, "XSession");
1220
1221     return TRUE;
1222 }
1223
1224 /* Loads language/layout/session info for user */
1225 static void
1226 load_user_values (LightDMUser *user)
1227 {
1228     load_dmrc (user);
1229     load_accounts_service (user); // overrides dmrc values
1230 }
1231
1232 /**
1233  * lightdm_user_get_language
1234  * @user: A #LightDMUser
1235  * 
1236  * Get the language for a user.
1237  * 
1238  * Return value: The language for the given user or #NULL if using system defaults.
1239  **/
1240 const gchar *
1241 lightdm_user_get_language (LightDMUser *user)
1242 {
1243     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1244     load_user_values (user);
1245     return GET_USER_PRIVATE (user)->language;
1246 }
1247
1248 /**
1249  * lightdm_user_get_layout
1250  * @user: A #LightDMUser
1251  * 
1252  * Get the keyboard layout for a user.
1253  * 
1254  * Return value: The keyboard layoyt for the given user or #NULL if using system defaults.
1255  **/
1256 const gchar *
1257 lightdm_user_get_layout (LightDMUser *user)
1258 {
1259     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1260     load_user_values (user);
1261     return GET_USER_PRIVATE (user)->layout;
1262 }
1263
1264 /**
1265  * lightdm_user_get_session
1266  * @user: A #LightDMUser
1267  * 
1268  * Get the session for a user.
1269  * 
1270  * Return value: The session for the given user or #NULL if using system defaults.
1271  **/
1272 const gchar *
1273 lightdm_user_get_session (LightDMUser *user)
1274 {
1275     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1276     load_user_values (user);
1277     return GET_USER_PRIVATE (user)->session; 
1278 }
1279
1280 /**
1281  * lightdm_user_get_logged_in:
1282  * @user: A #LightDMUser
1283  * 
1284  * Check if a user is logged in.
1285  * 
1286  * Return value: #TRUE if the user is currently logged in.
1287  **/
1288 gboolean
1289 lightdm_user_get_logged_in (LightDMUser *user)
1290 {
1291     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1292     LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1293     GList *link;
1294
1295     g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1296
1297     for (link = list_priv->sessions; link; link = link->next)
1298     {
1299         Session *session = link->data;
1300         if (strcmp (session->username, priv->name) == 0)
1301             return TRUE;
1302     }
1303
1304     return FALSE;
1305 }
1306
1307 static void
1308 lightdm_user_init (LightDMUser *user)
1309 {
1310 }
1311
1312 static void
1313 lightdm_user_set_property (GObject    *object,
1314                            guint       prop_id,
1315                            const GValue *value,
1316                            GParamSpec *pspec)
1317 {
1318     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1319 }
1320
1321 static void
1322 lightdm_user_get_property (GObject    *object,
1323                            guint       prop_id,
1324                            GValue     *value,
1325                            GParamSpec *pspec)
1326 {
1327     LightDMUser *self;
1328
1329     self = LIGHTDM_USER (object);
1330
1331     switch (prop_id)
1332     {
1333     case USER_PROP_NAME:
1334         g_value_set_string (value, lightdm_user_get_name (self));
1335         break;
1336     case USER_PROP_REAL_NAME:
1337         g_value_set_string (value, lightdm_user_get_real_name (self));
1338         break;
1339     case USER_PROP_DISPLAY_NAME:
1340         g_value_set_string (value, lightdm_user_get_display_name (self));
1341         break;
1342     case USER_PROP_HOME_DIRECTORY:
1343         g_value_set_string (value, lightdm_user_get_home_directory (self));
1344         break;
1345     case USER_PROP_IMAGE:
1346         g_value_set_string (value, lightdm_user_get_image (self));
1347         break;
1348     case USER_PROP_BACKGROUND:
1349         g_value_set_string (value, lightdm_user_get_background (self));
1350         break;
1351     case USER_PROP_LANGUAGE:
1352         g_value_set_string (value, lightdm_user_get_language (self));
1353         break;
1354     case USER_PROP_LAYOUT:
1355         g_value_set_string (value, lightdm_user_get_layout (self));
1356         break;
1357     case USER_PROP_SESSION:
1358         g_value_set_string (value, lightdm_user_get_session (self));
1359         break;
1360     case USER_PROP_LOGGED_IN:
1361         g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1362         break;
1363     default:
1364         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1365         break;
1366     }
1367 }
1368
1369 static void
1370 lightdm_user_finalize (GObject *object)
1371 {
1372     LightDMUser *self = LIGHTDM_USER (object);
1373     LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1374
1375     g_free (priv->name);
1376     g_free (priv->real_name);
1377     g_free (priv->home_directory);
1378     g_free (priv->image);
1379     g_free (priv->background);
1380     if (priv->dmrc_file)
1381         g_key_file_free (priv->dmrc_file);
1382 }
1383
1384 static void
1385 lightdm_user_class_init (LightDMUserClass *klass)
1386 {
1387     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1388   
1389     g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1390
1391     object_class->set_property = lightdm_user_set_property;
1392     object_class->get_property = lightdm_user_get_property;
1393     object_class->finalize = lightdm_user_finalize;
1394
1395     g_object_class_install_property (object_class,
1396                                      USER_PROP_NAME,
1397                                      g_param_spec_string ("name",
1398                                                           "name",
1399                                                           "Username",
1400                                                           NULL,
1401                                                           G_PARAM_READWRITE));
1402     g_object_class_install_property (object_class,
1403                                      USER_PROP_REAL_NAME,
1404                                      g_param_spec_string ("real-name",
1405                                                           "real-name",
1406                                                           "Users real name",
1407                                                           NULL,
1408                                                           G_PARAM_READWRITE));
1409     g_object_class_install_property (object_class,
1410                                      USER_PROP_DISPLAY_NAME,
1411                                      g_param_spec_string ("display-name",
1412                                                           "display-name",
1413                                                           "Users display name",
1414                                                           NULL,
1415                                                           G_PARAM_READABLE));
1416     g_object_class_install_property (object_class,
1417                                      USER_PROP_HOME_DIRECTORY,
1418                                      g_param_spec_string ("home-directory",
1419                                                           "home-directory",
1420                                                           "Home directory",
1421                                                           NULL,
1422                                                           G_PARAM_READWRITE));
1423     g_object_class_install_property (object_class,
1424                                      USER_PROP_IMAGE,
1425                                      g_param_spec_string ("image",
1426                                                           "image",
1427                                                           "Avatar image",
1428                                                           NULL,
1429                                                           G_PARAM_READWRITE));
1430     g_object_class_install_property (object_class,
1431                                      USER_PROP_BACKGROUND,
1432                                      g_param_spec_string ("background",
1433                                                           "background",
1434                                                           "User background",
1435                                                           NULL,
1436                                                           G_PARAM_READWRITE));
1437     g_object_class_install_property (object_class,
1438                                      USER_PROP_LANGUAGE,
1439                                      g_param_spec_string ("language",
1440                                                          "language",
1441                                                          "Language used by this user",
1442                                                          NULL,
1443                                                          G_PARAM_READABLE));
1444     g_object_class_install_property (object_class,
1445                                      USER_PROP_LAYOUT,
1446                                      g_param_spec_string ("layout",
1447                                                           "layout",
1448                                                           "Keyboard layout used by this user",
1449                                                           NULL,
1450                                                           G_PARAM_READABLE));
1451     g_object_class_install_property (object_class,
1452                                      USER_PROP_SESSION,
1453                                      g_param_spec_string ("session",
1454                                                           "session",
1455                                                           "Session used by this user",
1456                                                           NULL,
1457                                                           G_PARAM_READABLE));
1458     g_object_class_install_property (object_class,
1459                                      USER_PROP_LOGGED_IN,
1460                                      g_param_spec_boolean ("logged-in",
1461                                                            "logged-in",
1462                                                            "TRUE if the user is currently in a session",
1463                                                            FALSE,
1464                                                            G_PARAM_READWRITE));
1465
1466     /**
1467      * LightDMUser::changed:
1468      * @user: A #LightDMUser
1469      *
1470      * The ::changed signal gets emitted this user account is modified.
1471      **/
1472     user_signals[CHANGED] =
1473         g_signal_new ("changed",
1474                       G_TYPE_FROM_CLASS (klass),
1475                       G_SIGNAL_RUN_LAST,
1476                       G_STRUCT_OFFSET (LightDMUserClass, changed),
1477                       NULL, NULL,
1478                       g_cclosure_marshal_VOID__VOID,
1479                       G_TYPE_NONE, 0);
1480 }
1481
1482 static void
1483 session_init (Session *session)
1484 {
1485 }
1486
1487 static void
1488 session_finalize (GObject *object)
1489 {
1490     Session *self = SESSION (object);
1491
1492     g_free (self->path);
1493     g_free (self->username);
1494 }
1495
1496 static void
1497 session_class_init (SessionClass *klass)
1498 {
1499     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1500     object_class->finalize = session_finalize;
1501 }