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