]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/user.c
Merge with trunk
[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     if (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     gboolean system_account = FALSE;
451     GError *error = NULL;
452
453     /* Get the properties for this user */
454     if (!priv->changed_signal)
455         priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
456                                                                    "org.freedesktop.Accounts",
457                                                                    "org.freedesktop.Accounts.User",
458                                                                    "Changed",
459                                                                    priv->path,
460                                                                    NULL,
461                                                                    G_DBUS_SIGNAL_FLAGS_NONE,
462                                                                    accounts_user_changed_cb,
463                                                                    user,
464                                                                    NULL);
465     result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
466                                           "org.freedesktop.Accounts",
467                                           priv->path,
468                                           "org.freedesktop.DBus.Properties",
469                                           "GetAll",
470                                           g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
471                                           G_VARIANT_TYPE ("(a{sv})"),
472                                           G_DBUS_CALL_FLAGS_NONE,
473                                           -1,
474                                           NULL,
475                                           &error);
476     if (error)
477         g_warning ("Error updating user %s: %s", priv->path, error->message);
478     g_clear_error (&error);
479     if (!result)
480         return FALSE;
481
482     /* Store the properties we need */
483     g_variant_get (result, "(a{sv})", &iter);
484     while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
485     {
486         if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
487         {
488             g_free (priv->name);
489             priv->name = g_variant_dup_string (value, NULL);
490         }
491         else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
492         {
493             g_free (priv->real_name);
494             priv->real_name = g_variant_dup_string (value, NULL);
495         }
496         else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
497         {
498             g_free (priv->home_directory);
499             priv->home_directory = g_variant_dup_string (value, NULL);
500         }
501         else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
502             system_account = g_variant_get_boolean (value);
503         else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
504         {
505             if (priv->language)
506                 g_free (priv->language);
507             priv->language = g_variant_dup_string (value, NULL);
508         }
509         else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
510         {
511             g_free (priv->image);
512             priv->image = g_variant_dup_string (value, NULL);
513             if (strcmp (priv->image, "") == 0)
514             {
515                 g_free (priv->image);
516                 priv->image = NULL;
517             }
518         }
519         else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
520         {
521             g_free (priv->session);
522             priv->session = g_variant_dup_string (value, NULL);
523         }
524         else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
525         {
526             g_free (priv->background);
527             priv->background = g_variant_dup_string (value, NULL);
528             if (strcmp (priv->background, "") == 0)
529             {
530                 g_free (priv->background);
531                 priv->background = NULL;
532             }
533         }
534         else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
535         {
536             g_strfreev (priv->layouts);
537             priv->layouts = g_variant_dup_strv (value, NULL);
538             if (!priv->layouts)
539             {
540                 priv->layouts = g_malloc (sizeof (gchar *) * 1);
541                 priv->layouts[0] = NULL;
542             }
543         }
544         else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
545             priv->has_messages = g_variant_get_boolean (value);
546     }
547     g_variant_iter_free (iter);
548
549     g_variant_unref (result);
550
551     priv->loaded_values = TRUE;
552
553     return !system_account;
554 }
555
556 static void
557 add_accounts_user (LightDMUserList *user_list, const gchar *path, gboolean emit_signal)
558 {
559     LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
560     LightDMUser *user;
561     LightDMUserPrivate *priv;
562
563     user = g_object_new (LIGHTDM_TYPE_USER, NULL);
564     priv = GET_USER_PRIVATE (user);
565
566     g_debug ("User %s added", path);
567     priv->user_list = user_list;
568     priv->path = g_strdup (path);
569     g_signal_connect (user, "changed", G_CALLBACK (user_changed_cb), NULL);
570     if (load_accounts_user (user))
571     {
572         list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
573         if (emit_signal)      
574             g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
575     }
576     else
577         g_object_unref (user);
578 }
579
580 static void
581 accounts_user_added_cb (GDBusConnection *connection,
582                         const gchar *sender_name,
583                         const gchar *object_path,
584                         const gchar *interface_name,
585                         const gchar *signal_name,
586                         GVariant *parameters,
587                         gpointer data)
588 {
589     LightDMUserList *user_list = data;
590     gchar *path;
591     LightDMUser *user;
592   
593     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
594     {
595         g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
596         return;
597     }
598
599     g_variant_get (parameters, "(&o)", &path);
600
601     /* Add user if we haven't got them */
602     user = get_user_by_path (user_list, path);
603     if (!user)
604         add_accounts_user (user_list, path, TRUE);
605 }
606
607 static void
608 accounts_user_deleted_cb (GDBusConnection *connection,
609                           const gchar *sender_name,
610                           const gchar *object_path,
611                           const gchar *interface_name,
612                           const gchar *signal_name,
613                           GVariant *parameters,
614                           gpointer data)
615 {
616     LightDMUserList *user_list = data;
617     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
618     gchar *path;
619     LightDMUser *user;
620
621     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
622     {
623         g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
624         return;
625     }
626
627     g_variant_get (parameters, "(&o)", &path);
628
629     /* Delete user if we know of them */
630     user = get_user_by_path (user_list, path);
631     if (user)
632     {
633         g_debug ("User %s deleted", path);
634         priv->users = g_list_remove (priv->users, user);
635
636         g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
637
638         g_object_unref (user);
639     }
640 }
641
642 static Session *
643 load_session (LightDMUserList *user_list, const gchar *path)
644 {
645     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
646     Session *session = NULL;
647     GVariant *result, *username;
648     GError *error = NULL;
649
650     result = g_dbus_connection_call_sync (priv->bus,
651                                           "org.freedesktop.DisplayManager",
652                                           path,
653                                           "org.freedesktop.DBus.Properties",
654                                           "Get",
655                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
656                                           G_VARIANT_TYPE ("(v)"),
657                                           G_DBUS_CALL_FLAGS_NONE,
658                                           -1,
659                                           NULL,
660                                           &error);
661     if (error)
662         g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
663     g_clear_error (&error);
664     if (!result)
665         return NULL;
666
667     g_variant_get (result, "(v)", &username);
668     if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
669     {
670         gchar *name;
671
672         g_variant_get (username, "&s", &name);
673
674         g_debug ("Loaded session %s (%s)", path, name);
675         session = g_object_new (session_get_type (), NULL);
676         session->username = g_strdup (name);
677         session->path = g_strdup (path);
678         priv->sessions = g_list_append (priv->sessions, session);
679     }
680     g_variant_unref (username);
681     g_variant_unref (result);
682
683     return session;
684 }
685
686 static void
687 session_added_cb (GDBusConnection *connection,
688                   const gchar *sender_name,
689                   const gchar *object_path,
690                   const gchar *interface_name,
691                   const gchar *signal_name,
692                   GVariant *parameters,
693                   gpointer data)
694 {
695     LightDMUserList *user_list = data;
696     gchar *path;
697     Session *session;
698     LightDMUser *user = NULL;
699
700     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
701     {
702         g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
703         return;
704     }
705
706     g_variant_get (parameters, "(&o)", &path);
707     session = load_session (user_list, path);
708     if (session)
709         user = get_user_by_name (user_list, session->username);
710     if (user)
711         g_signal_emit (user, user_signals[CHANGED], 0);
712 }
713
714 static void
715 session_removed_cb (GDBusConnection *connection,
716                     const gchar *sender_name,
717                     const gchar *object_path,
718                     const gchar *interface_name,
719                     const gchar *signal_name,
720                     GVariant *parameters,
721                     gpointer data)
722 {
723     LightDMUserList *user_list = data;
724     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
725     gchar *path;
726     GList *link;
727
728     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
729     {
730         g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
731         return;
732     }
733
734     g_variant_get (parameters, "(&o)", &path);
735
736     for (link = priv->sessions; link; link = link->next)
737     {
738         Session *session = link->data;
739         if (strcmp (session->path, path) == 0)
740         {
741             LightDMUser *user;
742
743             g_debug ("Session %s removed", path);
744             priv->sessions = g_list_remove_link (priv->sessions, link);
745             user = get_user_by_name (user_list, session->username);
746             if (user)
747                 g_signal_emit (user, user_signals[CHANGED], 0);
748             g_object_unref (session);
749             break;
750         }
751     }
752 }
753
754 static void
755 load_users (LightDMUserList *user_list)
756 {
757     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
758     GVariant *result;
759     GError *error = NULL;
760
761     if (priv->have_users)
762         return;
763     priv->have_users = TRUE;
764
765     /* Get user list from accounts service and fall back to /etc/passwd if that fails */
766     priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
767                                                                   "org.freedesktop.Accounts",
768                                                                   "org.freedesktop.Accounts",
769                                                                   "UserAdded",
770                                                                   "/org/freedesktop/Accounts",
771                                                                   NULL,
772                                                                   G_DBUS_SIGNAL_FLAGS_NONE,
773                                                                   accounts_user_added_cb,
774                                                                   user_list,
775                                                                   NULL);
776     priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
777                                                                     "org.freedesktop.Accounts",
778                                                                     "org.freedesktop.Accounts",
779                                                                     "UserDeleted",
780                                                                     "/org/freedesktop/Accounts",
781                                                                     NULL,
782                                                                     G_DBUS_SIGNAL_FLAGS_NONE,
783                                                                     accounts_user_deleted_cb,
784                                                                     user_list,
785                                                                     NULL);
786     result = g_dbus_connection_call_sync (priv->bus,
787                                           "org.freedesktop.Accounts",
788                                           "/org/freedesktop/Accounts",
789                                           "org.freedesktop.Accounts",
790                                           "ListCachedUsers",
791                                           g_variant_new ("()"),
792                                           G_VARIANT_TYPE ("(ao)"),
793                                           G_DBUS_CALL_FLAGS_NONE,
794                                           -1,
795                                           NULL,
796                                           &error);
797     if (error)
798         g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
799     g_clear_error (&error);
800     if (result)
801     {
802         GVariantIter *iter;
803         const gchar *path;
804
805         g_debug ("Loading users from org.freedesktop.Accounts");
806         g_variant_get (result, "(ao)", &iter);
807         while (g_variant_iter_loop (iter, "&o", &path))
808             add_accounts_user (user_list, path, FALSE);
809         g_variant_iter_free (iter);
810         g_variant_unref (result);
811     }
812     else
813     {
814         GFile *passwd_file;
815
816         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
817         priv->user_added_signal = 0;
818         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
819         priv->user_removed_signal = 0;
820
821         load_passwd_file (user_list, FALSE);
822
823         /* Watch for changes to user list */
824
825         passwd_file = g_file_new_for_path (PASSWD_FILE);
826         priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
827         g_object_unref (passwd_file);
828         if (error)
829             g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
830         else
831             g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
832         g_clear_error (&error);
833     }
834
835     priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
836                                                                      "org.freedesktop.DisplayManager",
837                                                                      "org.freedesktop.DisplayManager",
838                                                                      "SessionAdded",
839                                                                      "/org/freedesktop/DisplayManager",
840                                                                      NULL,
841                                                                      G_DBUS_SIGNAL_FLAGS_NONE,
842                                                                      session_added_cb,
843                                                                      user_list,
844                                                                      NULL);
845     priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
846                                                                        "org.freedesktop.DisplayManager",
847                                                                        "org.freedesktop.DisplayManager",
848                                                                        "SessionRemoved",
849                                                                        "/org/freedesktop/DisplayManager",
850                                                                        NULL,
851                                                                        G_DBUS_SIGNAL_FLAGS_NONE,
852                                                                        session_removed_cb,
853
854                                                                     user_list,
855                                                                     NULL);
856     result = g_dbus_connection_call_sync (priv->bus,
857                                           "org.freedesktop.DisplayManager",
858                                           "/org/freedesktop/DisplayManager",
859                                           "org.freedesktop.DBus.Properties",
860                                           "Get",
861                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
862                                           G_VARIANT_TYPE ("(v)"),
863                                           G_DBUS_CALL_FLAGS_NONE,
864                                           -1,
865                                           NULL,
866                                           &error);
867     if (error)
868         g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
869     g_clear_error (&error);
870     if (result)
871     {
872         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
873         {
874             GVariant *value;
875             GVariantIter *iter;
876             const gchar *path;
877
878             g_variant_get (result, "(v)", &value);
879
880             g_debug ("Loading sessions from org.freedesktop.DisplayManager");
881             g_variant_get (value, "ao", &iter);
882             while (g_variant_iter_loop (iter, "&o", &path))
883                 load_session (user_list, path);
884             g_variant_iter_free (iter);
885
886             g_variant_unref (value);
887         }
888         else
889             g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
890
891         g_variant_unref (result);
892     }
893 }
894
895 /**
896  * lightdm_user_list_get_length:
897  * @user_list: a #LightDMUserList
898  *
899  * Return value: The number of users able to log in
900  **/
901 gint
902 lightdm_user_list_get_length (LightDMUserList *user_list)
903 {
904     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
905     load_users (user_list);
906     return g_list_length (GET_LIST_PRIVATE (user_list)->users);
907 }
908
909 /**
910  * lightdm_user_list_get_users:
911  * @user_list: A #LightDMUserList
912  *
913  * Get a list of users to present to the user.  This list may be a subset of the
914  * available users and may be empty depending on the server configuration.
915  *
916  * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
917  **/
918 GList *
919 lightdm_user_list_get_users (LightDMUserList *user_list)
920 {
921     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
922     load_users (user_list);
923     return GET_LIST_PRIVATE (user_list)->users;
924 }
925
926 /**
927  * lightdm_user_list_get_user_by_name:
928  * @user_list: A #LightDMUserList
929  * @username: Name of user to get.
930  *
931  * Get infomation about a given user or #NULL if this user doesn't exist.
932  *
933  * Return value: (transfer none): A #LightDMUser entry for the given user.
934  **/
935 LightDMUser *
936 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
937 {
938     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
939     g_return_val_if_fail (username != NULL, NULL);
940
941     load_users (user_list);
942
943     return get_user_by_name (user_list, username);
944 }
945
946 static void
947 lightdm_user_list_init (LightDMUserList *user_list)
948 {
949     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
950
951     priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
952 }
953
954 static void
955 lightdm_user_list_set_property (GObject    *object,
956                                 guint       prop_id,
957                                 const GValue *value,
958                                 GParamSpec *pspec)
959 {
960     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
961 }
962
963 static void
964 lightdm_user_list_get_property (GObject    *object,
965                                 guint       prop_id,
966                                 GValue     *value,
967                                 GParamSpec *pspec)
968 {
969     LightDMUserList *self;
970
971     self = LIGHTDM_USER_LIST (object);
972
973     switch (prop_id)
974     {
975     case LIST_PROP_NUM_USERS:
976         g_value_set_int (value, lightdm_user_list_get_length (self));
977         break;
978     default:
979         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
980         break;
981     }
982 }
983
984 static void
985 lightdm_user_list_finalize (GObject *object)
986 {
987     LightDMUserList *self = LIGHTDM_USER_LIST (object);
988     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
989
990     /* Remove children first, they might access us */
991     g_list_free_full (priv->users, g_object_unref);
992     g_list_free_full (priv->sessions, g_object_unref);
993
994     if (priv->user_added_signal)
995         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
996     if (priv->user_removed_signal)
997         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
998     if (priv->session_added_signal)
999         g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1000     if (priv->session_removed_signal)
1001         g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1002     g_object_unref (priv->bus);
1003     if (priv->passwd_monitor)
1004         g_object_unref (priv->passwd_monitor);
1005
1006     G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
1007 }
1008
1009 static void
1010 lightdm_user_list_class_init (LightDMUserListClass *klass)
1011 {
1012     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1013
1014     g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
1015
1016     object_class->set_property = lightdm_user_list_set_property;
1017     object_class->get_property = lightdm_user_list_get_property;
1018     object_class->finalize = lightdm_user_list_finalize;
1019
1020     g_object_class_install_property (object_class,
1021                                      LIST_PROP_NUM_USERS,
1022                                      g_param_spec_int ("num-users",
1023                                                        "num-users",
1024                                                        "Number of login users",
1025                                                        0, G_MAXINT, 0,
1026                                                        G_PARAM_READABLE));
1027     /**
1028      * LightDMUserList::user-added:
1029      * @user_list: A #LightDMUserList
1030      * @user: The #LightDM user that has been added.
1031      *
1032      * The ::user-added signal gets emitted when a user account is created.
1033      **/
1034     list_signals[USER_ADDED] =
1035         g_signal_new ("user-added",
1036                       G_TYPE_FROM_CLASS (klass),
1037                       G_SIGNAL_RUN_LAST,
1038                       G_STRUCT_OFFSET (LightDMUserListClass, user_added),
1039                       NULL, NULL,
1040                       NULL,
1041                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1042
1043     /**
1044      * LightDMUserList::user-changed:
1045      * @user_list: A #LightDMUserList
1046      * @user: The #LightDM user that has been changed.
1047      *
1048      * The ::user-changed signal gets emitted when a user account is modified.
1049      **/
1050     list_signals[USER_CHANGED] =
1051         g_signal_new ("user-changed",
1052                       G_TYPE_FROM_CLASS (klass),
1053                       G_SIGNAL_RUN_LAST,
1054                       G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
1055                       NULL, NULL,
1056                       NULL,
1057                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1058
1059     /**
1060      * LightDMUserList::user-removed:
1061      * @user_list: A #LightDMUserList
1062      * @user: The #LightDM user that has been removed.
1063      *
1064      * The ::user-removed signal gets emitted when a user account is removed.
1065      **/
1066     list_signals[USER_REMOVED] =
1067         g_signal_new ("user-removed",
1068                       G_TYPE_FROM_CLASS (klass),
1069                       G_SIGNAL_RUN_LAST,
1070                       G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1071                       NULL, NULL,
1072                       NULL,
1073                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1074 }
1075
1076 static void
1077 load_dmrc (LightDMUser *user)
1078 {
1079     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1080     gchar *path;
1081     //gboolean have_dmrc;
1082
1083     if (!priv->dmrc_file)
1084         priv->dmrc_file = g_key_file_new ();
1085
1086     /* Load from the user directory */  
1087     path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1088     /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1089     g_free (path);
1090
1091     /* If no ~/.dmrc, then load from the cache */
1092     // FIXME
1093
1094     // FIXME: Watch for changes
1095
1096     /* The Language field contains the locale */
1097     if (priv->language)
1098         g_free (priv->language);
1099     priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1100
1101     if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1102     {
1103         g_strfreev (priv->layouts);
1104         priv->layouts = g_malloc (sizeof (gchar *) * 2);
1105         priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1106         priv->layouts[1] = NULL;
1107     }
1108
1109     if (priv->session)
1110         g_free (priv->session);
1111     priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1112 }
1113
1114 /* Loads language/layout/session info for user */
1115 static void
1116 load_user_values (LightDMUser *user)
1117 {
1118     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1119
1120     if (priv->loaded_values)
1121         return;
1122     priv->loaded_values = TRUE;
1123
1124     if (!priv->path)
1125         load_dmrc (user);
1126 }
1127
1128 /**
1129  * lightdm_user_get_name:
1130  * @user: A #LightDMUser
1131  * 
1132  * Get the name of a user.
1133  * 
1134  * Return value: The name of the given user
1135  **/
1136 const gchar *
1137 lightdm_user_get_name (LightDMUser *user)
1138 {
1139     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1140     load_user_values (user);
1141     return GET_USER_PRIVATE (user)->name;
1142 }
1143
1144 /**
1145  * lightdm_user_get_real_name:
1146  * @user: A #LightDMUser
1147  * 
1148  * Get the real name of a user.
1149  *
1150  * Return value: The real name of the given user
1151  **/
1152 const gchar *
1153 lightdm_user_get_real_name (LightDMUser *user)
1154 {
1155     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1156     load_user_values (user);
1157     return GET_USER_PRIVATE (user)->real_name;
1158 }
1159
1160 /**
1161  * lightdm_user_get_display_name:
1162  * @user: A #LightDMUser
1163  * 
1164  * Get the display name of a user.
1165  * 
1166  * Return value: The display name of the given user
1167  **/
1168 const gchar *
1169 lightdm_user_get_display_name (LightDMUser *user)
1170 {
1171     LightDMUserPrivate *priv;
1172
1173     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1174
1175     load_user_values (user);
1176
1177     priv = GET_USER_PRIVATE (user);
1178     if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1179         return priv->name;
1180     else
1181         return priv->real_name;
1182 }
1183
1184 /**
1185  * lightdm_user_get_home_directory:
1186  * @user: A #LightDMUser
1187  * 
1188  * Get the home directory for a user.
1189  * 
1190  * Return value: The users home directory
1191  */
1192 const gchar *
1193 lightdm_user_get_home_directory (LightDMUser *user)
1194 {
1195     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1196     load_user_values (user);
1197     return GET_USER_PRIVATE (user)->home_directory;
1198 }
1199
1200 /**
1201  * lightdm_user_get_image:
1202  * @user: A #LightDMUser
1203  * 
1204  * Get the image URI for a user.
1205  * 
1206  * Return value: The image URI for the given user or #NULL if no URI
1207  **/
1208 const gchar *
1209 lightdm_user_get_image (LightDMUser *user)
1210 {
1211     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1212     load_user_values (user);
1213     return GET_USER_PRIVATE (user)->image;
1214 }
1215
1216 /**
1217  * lightdm_user_get_background:
1218  * @user: A #LightDMUser
1219  * 
1220  * Get the background file path for a user.
1221  * 
1222  * Return value: The background file path for the given user or #NULL if no path
1223  **/
1224 const gchar *
1225 lightdm_user_get_background (LightDMUser *user)
1226 {
1227     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1228     load_user_values (user);
1229     return GET_USER_PRIVATE (user)->background;
1230 }
1231
1232 /**
1233  * lightdm_user_get_language:
1234  * @user: A #LightDMUser
1235  * 
1236  * Get the language for a user.
1237  * 
1238  * Return value: The language 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.
1239  **/
1240 const gchar *
1241 lightdm_user_get_language (LightDMUser *user)
1242 {
1243     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1244     load_user_values (user);
1245     return GET_USER_PRIVATE (user)->language;
1246 }
1247
1248 /**
1249  * lightdm_user_get_layout:
1250  * @user: A #LightDMUser
1251  * 
1252  * Get the keyboard layout for a user.
1253  * 
1254  * Return value: The keyboard layout for the given user or #NULL if using system defaults.  Copy the value if you want to use it long term.
1255  **/
1256 const gchar *
1257 lightdm_user_get_layout (LightDMUser *user)
1258 {
1259     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1260     load_user_values (user);
1261     return GET_USER_PRIVATE (user)->layouts[0];
1262 }
1263
1264 /**
1265  * lightdm_user_get_layouts:
1266  * @user: A #LightDMUser
1267  * 
1268  * Get the configured keyboard layouts for a user.
1269  * 
1270  * 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.
1271  **/
1272 const gchar * const *
1273 lightdm_user_get_layouts (LightDMUser *user)
1274 {
1275     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1276     load_user_values (user);
1277     return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1278 }
1279
1280 /**
1281  * lightdm_user_get_session:
1282  * @user: A #LightDMUser
1283  * 
1284  * Get the session for a user.
1285  * 
1286  * Return value: The session for the given user or #NULL if using system defaults.
1287  **/
1288 const gchar *
1289 lightdm_user_get_session (LightDMUser *user)
1290 {
1291     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1292     load_user_values (user);
1293     return GET_USER_PRIVATE (user)->session; 
1294 }
1295
1296 /**
1297  * lightdm_user_get_logged_in:
1298  * @user: A #LightDMUser
1299  * 
1300  * Check if a user is logged in.
1301  * 
1302  * Return value: #TRUE if the user is currently logged in.
1303  **/
1304 gboolean
1305 lightdm_user_get_logged_in (LightDMUser *user)
1306 {
1307     LightDMUserPrivate *priv;
1308     LightDMUserListPrivate *list_priv;
1309     GList *link;
1310
1311     g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1312
1313     priv = GET_USER_PRIVATE (user);
1314     list_priv = GET_LIST_PRIVATE (priv->user_list);
1315
1316     for (link = list_priv->sessions; link; link = link->next)
1317     {
1318         Session *session = link->data;
1319         if (strcmp (session->username, priv->name) == 0)
1320             return TRUE;
1321     }
1322
1323     return FALSE;
1324 }
1325
1326 /**
1327  * lightdm_user_get_has_messages:
1328  * @user: A #LightDMUser
1329  * 
1330  * Check if a user has waiting messages.
1331  * 
1332  * Return value: #TRUE if the user has waiting messages.
1333  **/
1334 gboolean
1335 lightdm_user_get_has_messages (LightDMUser *user)
1336 {
1337     g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1338     load_user_values (user);
1339     return GET_USER_PRIVATE (user)->has_messages;
1340 }
1341
1342 static void
1343 lightdm_user_init (LightDMUser *user)
1344 {
1345     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1346     priv->layouts = g_malloc (sizeof (gchar *) * 1);
1347     priv->layouts[0] = NULL;
1348 }
1349
1350 static void
1351 lightdm_user_set_property (GObject    *object,
1352                            guint       prop_id,
1353                            const GValue *value,
1354                            GParamSpec *pspec)
1355 {
1356     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1357 }
1358
1359 static void
1360 lightdm_user_get_property (GObject    *object,
1361                            guint       prop_id,
1362                            GValue     *value,
1363                            GParamSpec *pspec)
1364 {
1365     LightDMUser *self;
1366
1367     self = LIGHTDM_USER (object);
1368
1369     switch (prop_id)
1370     {
1371     case USER_PROP_NAME:
1372         g_value_set_string (value, lightdm_user_get_name (self));
1373         break;
1374     case USER_PROP_REAL_NAME:
1375         g_value_set_string (value, lightdm_user_get_real_name (self));
1376         break;
1377     case USER_PROP_DISPLAY_NAME:
1378         g_value_set_string (value, lightdm_user_get_display_name (self));
1379         break;
1380     case USER_PROP_HOME_DIRECTORY:
1381         g_value_set_string (value, lightdm_user_get_home_directory (self));
1382         break;
1383     case USER_PROP_IMAGE:
1384         g_value_set_string (value, lightdm_user_get_image (self));
1385         break;
1386     case USER_PROP_BACKGROUND:
1387         g_value_set_string (value, lightdm_user_get_background (self));
1388         break;
1389     case USER_PROP_LANGUAGE:
1390         g_value_set_string (value, lightdm_user_get_language (self));
1391         break;
1392     case USER_PROP_LAYOUT:
1393         g_value_set_string (value, lightdm_user_get_layout (self));
1394         break;
1395     case USER_PROP_LAYOUTS:
1396         g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
1397         break;
1398     case USER_PROP_SESSION:
1399         g_value_set_string (value, lightdm_user_get_session (self));
1400         break;
1401     case USER_PROP_LOGGED_IN:
1402         g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1403         break;
1404     case USER_PROP_HAS_MESSAGES:
1405         g_value_set_boolean (value, lightdm_user_get_has_messages (self));
1406         break;
1407     default:
1408         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1409         break;
1410     }
1411 }
1412
1413 static void
1414 lightdm_user_finalize (GObject *object)
1415 {
1416     LightDMUser *self = LIGHTDM_USER (object);
1417     LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1418
1419     g_free (priv->path);
1420     if (priv->changed_signal)
1421         g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1422     g_free (priv->name);
1423     g_free (priv->real_name);
1424     g_free (priv->home_directory);
1425     g_free (priv->image);
1426     g_free (priv->background);
1427     g_strfreev (priv->layouts);
1428     if (priv->dmrc_file)
1429         g_key_file_free (priv->dmrc_file);
1430 }
1431
1432 static void
1433 lightdm_user_class_init (LightDMUserClass *klass)
1434 {
1435     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1436   
1437     g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1438
1439     object_class->set_property = lightdm_user_set_property;
1440     object_class->get_property = lightdm_user_get_property;
1441     object_class->finalize = lightdm_user_finalize;
1442
1443     g_object_class_install_property (object_class,
1444                                      USER_PROP_NAME,
1445                                      g_param_spec_string ("name",
1446                                                           "name",
1447                                                           "Username",
1448                                                           NULL,
1449                                                           G_PARAM_READWRITE));
1450     g_object_class_install_property (object_class,
1451                                      USER_PROP_REAL_NAME,
1452                                      g_param_spec_string ("real-name",
1453                                                           "real-name",
1454                                                           "Users real name",
1455                                                           NULL,
1456                                                           G_PARAM_READWRITE));
1457     g_object_class_install_property (object_class,
1458                                      USER_PROP_DISPLAY_NAME,
1459                                      g_param_spec_string ("display-name",
1460                                                           "display-name",
1461                                                           "Users display name",
1462                                                           NULL,
1463                                                           G_PARAM_READABLE));
1464     g_object_class_install_property (object_class,
1465                                      USER_PROP_HOME_DIRECTORY,
1466                                      g_param_spec_string ("home-directory",
1467                                                           "home-directory",
1468                                                           "Home directory",
1469                                                           NULL,
1470                                                           G_PARAM_READWRITE));
1471     g_object_class_install_property (object_class,
1472                                      USER_PROP_IMAGE,
1473                                      g_param_spec_string ("image",
1474                                                           "image",
1475                                                           "Avatar image",
1476                                                           NULL,
1477                                                           G_PARAM_READWRITE));
1478     g_object_class_install_property (object_class,
1479                                      USER_PROP_BACKGROUND,
1480                                      g_param_spec_string ("background",
1481                                                           "background",
1482                                                           "User background",
1483                                                           NULL,
1484                                                           G_PARAM_READWRITE));
1485     g_object_class_install_property (object_class,
1486                                      USER_PROP_LANGUAGE,
1487                                      g_param_spec_string ("language",
1488                                                          "language",
1489                                                          "Language used by this user",
1490                                                          NULL,
1491                                                          G_PARAM_READABLE));
1492     g_object_class_install_property (object_class,
1493                                      USER_PROP_LAYOUT,
1494                                      g_param_spec_string ("layout",
1495                                                           "layout",
1496                                                           "Keyboard layout used by this user",
1497                                                           NULL,
1498                                                           G_PARAM_READABLE));
1499     g_object_class_install_property (object_class,
1500                                      USER_PROP_LAYOUTS,
1501                                      g_param_spec_boxed ("layouts",
1502                                                          "layouts",
1503                                                          "Keyboard layouts used by this user",
1504                                                          G_TYPE_STRV,
1505                                                          G_PARAM_READABLE));
1506     g_object_class_install_property (object_class,
1507                                      USER_PROP_SESSION,
1508                                      g_param_spec_string ("session",
1509                                                           "session",
1510                                                           "Session used by this user",
1511                                                           NULL,
1512                                                           G_PARAM_READABLE));
1513     g_object_class_install_property (object_class,
1514                                      USER_PROP_LOGGED_IN,
1515                                      g_param_spec_boolean ("logged-in",
1516                                                            "logged-in",
1517                                                            "TRUE if the user is currently in a session",
1518                                                            FALSE,
1519                                                            G_PARAM_READWRITE));
1520     g_object_class_install_property (object_class,
1521                                      USER_PROP_LOGGED_IN,
1522                                      g_param_spec_boolean ("has-messages",
1523                                                            "has-messages",
1524                                                            "TRUE if the user is has waiting messages",
1525                                                            FALSE,
1526                                                            G_PARAM_READWRITE));
1527
1528     /**
1529      * LightDMUser::changed:
1530      * @user: A #LightDMUser
1531      *
1532      * The ::changed signal gets emitted this user account is modified.
1533      **/
1534     user_signals[CHANGED] =
1535         g_signal_new ("changed",
1536                       G_TYPE_FROM_CLASS (klass),
1537                       G_SIGNAL_RUN_LAST,
1538                       G_STRUCT_OFFSET (LightDMUserClass, changed),
1539                       NULL, NULL,
1540                       NULL,
1541                       G_TYPE_NONE, 0);
1542 }
1543
1544 static void
1545 session_init (Session *session)
1546 {
1547 }
1548
1549 static void
1550 session_finalize (GObject *object)
1551 {
1552     Session *self = SESSION (object);
1553
1554     g_free (self->path);
1555     g_free (self->username);
1556 }
1557
1558 static void
1559 session_class_init (SessionClass *klass)
1560 {
1561     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1562     object_class->finalize = session_finalize;
1563 }