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