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