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