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