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