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