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