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