]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - common/user-list.c
Don't load .dmrc files unless we need the data from them
[sojka/lightdm.git] / common / user-list.c
1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
2  *
3  * Copyright (C) 2010 Robert Ancell.
4  * Copyright (C) 2014 Canonical, Ltd.
5  * Authors: Robert Ancell <robert.ancell@canonical.com>
6  *          Michael Terry <michael.terry@canonical.com>
7  * 
8  * This library is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 2 or version 3 of the License.
11  * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
12  */
13
14 #include <config.h>
15
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/utsname.h>
19 #include <pwd.h>
20 #include <gio/gio.h>
21
22 #include "dmrc.h"
23 #include "user-list.h"
24
25 enum
26 {
27     LIST_PROP_0,
28     LIST_PROP_NUM_USERS,
29     LIST_PROP_USERS,
30 };
31
32 enum
33 {
34     USER_PROP_0,
35     USER_PROP_NAME,
36     USER_PROP_REAL_NAME,
37     USER_PROP_DISPLAY_NAME,
38     USER_PROP_HOME_DIRECTORY,
39     USER_PROP_SHELL,
40     USER_PROP_IMAGE,
41     USER_PROP_BACKGROUND,
42     USER_PROP_LANGUAGE,
43     USER_PROP_LAYOUT,
44     USER_PROP_LAYOUTS,
45     USER_PROP_SESSION,
46     USER_PROP_LOGGED_IN,
47     USER_PROP_HAS_MESSAGES,
48     USER_PROP_UID,
49     USER_PROP_GID,
50 };
51
52 enum
53 {
54     USER_ADDED,
55     USER_CHANGED,
56     USER_REMOVED,
57     LAST_LIST_SIGNAL
58 };
59 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
60
61 enum
62 {
63     CHANGED,
64     LAST_USER_SIGNAL
65 };
66 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
67
68 typedef struct
69 {
70     /* Bus connection being communicated on */
71     GDBusConnection *bus;
72
73     /* D-Bus signals for accounts service events */
74     guint user_added_signal;
75     guint user_removed_signal;
76
77     /* D-Bus signals for display manager events */
78     guint session_added_signal;
79     guint session_removed_signal;
80
81     /* File monitor for password file */
82     GFileMonitor *passwd_monitor;
83
84     /* TRUE if have scanned users */
85     gboolean have_users;
86
87     /* List of users */
88     GList *users;
89
90     /* List of sessions */
91     GList *sessions;
92 } CommonUserListPrivate;
93
94 typedef struct
95 {
96     /* User list this user is part of */
97     CommonUserList *user_list;
98
99     /* TRUE if have loaded the DMRC file */
100     gboolean loaded_dmrc;
101
102     /* Accounts service path */
103     gchar *path;
104
105     /* Update signal from accounts service */
106     guint changed_signal;
107
108     /* Username */
109     gchar *name;
110
111     /* Descriptive name for user */
112     gchar *real_name;
113
114     /* Home directory of user */
115     gchar *home_directory;
116
117     /* Shell for user */
118     gchar *shell;
119
120     /* Image for user */
121     gchar *image;
122
123     /* Background image for users */
124     gchar *background;
125
126     /* TRUE if this user has messages available */
127     gboolean has_messages;
128
129     /* UID of user */
130     guint64 uid;
131
132     /* GID of user */
133     guint64 gid;
134
135     /* User chosen language */
136     gchar *language;
137
138     /* User layout preferences */
139     gchar **layouts;
140
141     /* User default session */
142     gchar *session;
143 } CommonUserPrivate;
144
145 typedef struct
146 {
147     GObject parent_instance;
148     gchar *path;
149     gchar *username;
150 } CommonSession;
151
152 typedef struct
153 {
154     GObjectClass parent_class;
155 } CommonSessionClass;
156
157 G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
158 G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
159 #define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
160 GType common_session_get_type (void);
161 G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT);
162
163 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
164 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
165
166 #define PASSWD_FILE      "/etc/passwd"
167 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
168
169 static CommonUserList *singleton = NULL;
170
171 /**
172  * common_user_list_get_instance:
173  *
174  * Get the user list.
175  *
176  * Return value: (transfer none): the #CommonUserList
177  **/
178 CommonUserList *
179 common_user_list_get_instance (void)
180 {
181     if (!singleton)
182         singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
183     return singleton;
184 }
185
186 void
187 common_user_list_cleanup (void)
188 {
189     if (singleton)
190         g_object_unref (singleton);
191     singleton = NULL;
192 }
193
194 static CommonUser *
195 get_user_by_name (CommonUserList *user_list, const gchar *username)
196 {
197     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
198     GList *link;
199   
200     for (link = priv->users; link; link = link->next)
201     {
202         CommonUser *user = link->data;
203         if (g_strcmp0 (common_user_get_name (user), username) == 0)
204             return user;
205     }
206
207     return NULL;
208 }
209
210 static CommonUser *
211 get_user_by_path (CommonUserList *user_list, const gchar *path)
212 {
213     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
214     GList *link;
215   
216     for (link = priv->users; link; link = link->next)
217     {
218         CommonUser *user = link->data;
219         if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
220             return user;
221     }
222
223     return NULL;
224 }
225   
226 static gint
227 compare_user (gconstpointer a, gconstpointer b)
228 {
229     CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
230     return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
231 }
232
233 static gboolean
234 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
235 {
236     CommonUserPrivate *priv = GET_USER_PRIVATE (user);
237
238     /* Skip if already set to this */
239     if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
240         g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
241         g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
242         g_strcmp0 (common_user_get_image (user), image) == 0)
243         return FALSE;
244
245     g_free (priv->real_name);
246     priv->real_name = g_strdup (real_name);
247     g_free (priv->home_directory);
248     priv->home_directory = g_strdup (home_directory);
249     g_free (priv->shell);
250     priv->shell = g_strdup (shell);
251     g_free (priv->image);
252     priv->image = g_strdup (image);
253
254     return TRUE;
255 }
256
257 static void
258 user_changed_cb (CommonUser *user)
259 {
260     g_signal_emit (GET_USER_PRIVATE (user)->user_list, list_signals[USER_CHANGED], 0, user);
261 }
262
263 static CommonUser *
264 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
265 {
266     CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
267     CommonUserPrivate *priv = GET_USER_PRIVATE (user);
268     char **tokens;
269     gchar *real_name, *image;
270
271     tokens = g_strsplit (entry->pw_gecos, ",", -1);
272     if (tokens[0] != NULL && tokens[0][0] != '\0')
273         real_name = g_strdup (tokens[0]);
274     else
275         real_name = g_strdup ("");
276     g_strfreev (tokens);
277
278     image = g_build_filename (entry->pw_dir, ".face", NULL);
279     if (!g_file_test (image, G_FILE_TEST_EXISTS))
280     {
281         g_free (image);
282         image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
283         if (!g_file_test (image, G_FILE_TEST_EXISTS))
284         {
285             g_free (image);
286             image = NULL;
287         }
288     }
289
290     priv->user_list = user_list;
291     priv->name = g_strdup (entry->pw_name);
292     priv->real_name = real_name;
293     priv->home_directory = g_strdup (entry->pw_dir);
294     priv->shell = g_strdup (entry->pw_shell);
295     priv->image = image;
296     priv->uid = entry->pw_uid;
297     priv->gid = entry->pw_gid;
298
299     return user;
300 }
301
302 static void
303 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
304 {
305     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
306     GKeyFile *config;
307     gchar *value;
308     gint minimum_uid;
309     gchar **hidden_users, **hidden_shells;
310     GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
311     GError *error = NULL;
312
313     g_debug ("Loading user config from %s", USER_CONFIG_FILE);
314
315     config = g_key_file_new ();
316     g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
317     if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
318         g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
319     g_clear_error (&error);
320
321     if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
322         minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
323     else
324         minimum_uid = 500;
325
326     value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
327     if (!value)
328         value = g_strdup ("nobody nobody4 noaccess");
329     hidden_users = g_strsplit (value, " ", -1);
330     g_free (value);
331
332     value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
333     if (!value)
334         value = g_strdup ("/bin/false /usr/sbin/nologin");
335     hidden_shells = g_strsplit (value, " ", -1);
336     g_free (value);
337
338     g_key_file_free (config);
339
340     setpwent ();
341
342     while (TRUE)
343     {
344         struct passwd *entry;
345         CommonUser *user;
346         int i;
347
348         errno = 0;
349         entry = getpwent ();
350         if (!entry)
351             break;
352
353         /* Ignore system users */
354         if (entry->pw_uid < minimum_uid)
355             continue;
356
357         /* Ignore users disabled by shell */
358         if (entry->pw_shell)
359         {
360             for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
361             if (hidden_shells[i])
362                 continue;
363         }
364
365         /* Ignore certain users */
366         for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
367         if (hidden_users[i])
368             continue;
369
370         user = make_passwd_user (user_list, entry);
371
372         /* Update existing users if have them */
373         for (link = priv->users; link; link = link->next)
374         {
375             CommonUser *info = link->data;
376             if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
377             {
378                 if (update_passwd_user (info, common_user_get_real_name (user), common_user_get_home_directory (user), common_user_get_shell (user), common_user_get_image (user)))
379                     changed_users = g_list_insert_sorted (changed_users, info, compare_user);
380                 g_object_unref (user);
381                 user = info;
382                 break;
383             }
384         }
385         if (!link)
386         {
387             /* Only notify once we have loaded the user list */
388             if (priv->have_users)
389                 new_users = g_list_insert_sorted (new_users, user, compare_user);
390         }
391         users = g_list_insert_sorted (users, user, compare_user);
392     }
393     g_strfreev (hidden_users);
394     g_strfreev (hidden_shells);
395
396     if (errno != 0)
397         g_warning ("Failed to read password database: %s", strerror (errno));
398
399     endpwent ();
400
401     /* Use new user list */
402     old_users = priv->users;
403     priv->users = users;
404   
405     /* Notify of changes */
406     for (link = new_users; link; link = link->next)
407     {
408         CommonUser *info = link->data;
409         g_debug ("User %s added", common_user_get_name (info));
410         g_signal_connect (info, "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     return !system_account;
589 }
590
591 static void
592 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
593 {
594     CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
595     CommonUser *user;
596     CommonUserPrivate *priv;
597
598     user = g_object_new (COMMON_TYPE_USER, NULL);
599     priv = GET_USER_PRIVATE (user);
600
601     g_debug ("User %s added", path);
602     priv->user_list = user_list;
603     priv->path = g_strdup (path);
604     g_signal_connect (user, "changed", G_CALLBACK (user_changed_cb), NULL);
605     if (load_accounts_user (user))
606     {
607         list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
608         if (emit_signal)      
609             g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
610     }
611     else
612         g_object_unref (user);
613 }
614
615 static void
616 accounts_user_added_cb (GDBusConnection *connection,
617                         const gchar *sender_name,
618                         const gchar *object_path,
619                         const gchar *interface_name,
620                         const gchar *signal_name,
621                         GVariant *parameters,
622                         gpointer data)
623 {
624     CommonUserList *user_list = data;
625     gchar *path;
626     CommonUser *user;
627   
628     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
629     {
630         g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
631         return;
632     }
633
634     g_variant_get (parameters, "(&o)", &path);
635
636     /* Add user if we haven't got them */
637     user = get_user_by_path (user_list, path);
638     if (!user)
639         add_accounts_user (user_list, path, TRUE);
640 }
641
642 static void
643 accounts_user_deleted_cb (GDBusConnection *connection,
644                           const gchar *sender_name,
645                           const gchar *object_path,
646                           const gchar *interface_name,
647                           const gchar *signal_name,
648                           GVariant *parameters,
649                           gpointer data)
650 {
651     CommonUserList *user_list = data;
652     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
653     gchar *path;
654     CommonUser *user;
655
656     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
657     {
658         g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
659         return;
660     }
661
662     g_variant_get (parameters, "(&o)", &path);
663
664     /* Delete user if we know of them */
665     user = get_user_by_path (user_list, path);
666     if (user)
667     {
668         g_debug ("User %s deleted", path);
669         priv->users = g_list_remove (priv->users, user);
670
671         g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
672
673         g_object_unref (user);
674     }
675 }
676
677 static CommonSession *
678 load_session (CommonUserList *user_list, const gchar *path)
679 {
680     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
681     CommonSession *session = NULL;
682     GVariant *result, *username;
683     GError *error = NULL;
684
685     result = g_dbus_connection_call_sync (priv->bus,
686                                           "org.freedesktop.DisplayManager",
687                                           path,
688                                           "org.freedesktop.DBus.Properties",
689                                           "Get",
690                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
691                                           G_VARIANT_TYPE ("(v)"),
692                                           G_DBUS_CALL_FLAGS_NONE,
693                                           -1,
694                                           NULL,
695                                           &error);
696     if (error)
697         g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
698     g_clear_error (&error);
699     if (!result)
700         return NULL;
701
702     g_variant_get (result, "(v)", &username);
703     if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
704     {
705         gchar *name;
706
707         g_variant_get (username, "&s", &name);
708
709         g_debug ("Loaded session %s (%s)", path, name);
710         session = g_object_new (common_session_get_type (), NULL);
711         session->username = g_strdup (name);
712         session->path = g_strdup (path);
713         priv->sessions = g_list_append (priv->sessions, session);
714     }
715     g_variant_unref (username);
716     g_variant_unref (result);
717
718     return session;
719 }
720
721 static void
722 session_added_cb (GDBusConnection *connection,
723                   const gchar *sender_name,
724                   const gchar *object_path,
725                   const gchar *interface_name,
726                   const gchar *signal_name,
727                   GVariant *parameters,
728                   gpointer data)
729 {
730     CommonUserList *user_list = data;
731     gchar *path;
732     CommonSession *session;
733     CommonUser *user = NULL;
734
735     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
736     {
737         g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
738         return;
739     }
740
741     g_variant_get (parameters, "(&o)", &path);
742     session = load_session (user_list, path);
743     if (session)
744         user = get_user_by_name (user_list, session->username);
745     if (user)
746         g_signal_emit (user, user_signals[CHANGED], 0);
747 }
748
749 static void
750 session_removed_cb (GDBusConnection *connection,
751                     const gchar *sender_name,
752                     const gchar *object_path,
753                     const gchar *interface_name,
754                     const gchar *signal_name,
755                     GVariant *parameters,
756                     gpointer data)
757 {
758     CommonUserList *user_list = data;
759     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
760     gchar *path;
761     GList *link;
762
763     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
764     {
765         g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
766         return;
767     }
768
769     g_variant_get (parameters, "(&o)", &path);
770
771     for (link = priv->sessions; link; link = link->next)
772     {
773         CommonSession *session = link->data;
774         if (strcmp (session->path, path) == 0)
775         {
776             CommonUser *user;
777
778             g_debug ("Session %s removed", path);
779             priv->sessions = g_list_delete_link (priv->sessions, link);
780             user = get_user_by_name (user_list, session->username);
781             if (user)
782                 g_signal_emit (user, user_signals[CHANGED], 0);
783             g_object_unref (session);
784             break;
785         }
786     }
787 }
788
789 static void
790 load_sessions (CommonUserList *user_list)
791 {
792     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
793     GVariant *result;
794     GError *error = NULL;
795
796     priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
797                                                                      "org.freedesktop.DisplayManager",
798                                                                      "org.freedesktop.DisplayManager",
799                                                                      "SessionAdded",
800                                                                      "/org/freedesktop/DisplayManager",
801                                                                      NULL,
802                                                                      G_DBUS_SIGNAL_FLAGS_NONE,
803                                                                      session_added_cb,
804                                                                      user_list,
805                                                                      NULL);
806     priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
807                                                                        "org.freedesktop.DisplayManager",
808                                                                        "org.freedesktop.DisplayManager",
809                                                                        "SessionRemoved",
810                                                                        "/org/freedesktop/DisplayManager",
811                                                                        NULL,
812                                                                        G_DBUS_SIGNAL_FLAGS_NONE,
813                                                                        session_removed_cb,
814                                                                        user_list,
815                                                                        NULL);
816     result = g_dbus_connection_call_sync (priv->bus,
817                                           "org.freedesktop.DisplayManager",
818                                           "/org/freedesktop/DisplayManager",
819                                           "org.freedesktop.DBus.Properties",
820                                           "Get",
821                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
822                                           G_VARIANT_TYPE ("(v)"),
823                                           G_DBUS_CALL_FLAGS_NONE,
824                                           -1,
825                                           NULL,
826                                           &error);
827     if (error)
828         g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
829     g_clear_error (&error);
830     if (result)
831     {
832         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
833         {
834             GVariant *value;
835             GVariantIter *iter;
836             const gchar *path;
837
838             g_variant_get (result, "(v)", &value);
839
840             g_debug ("Loading sessions from org.freedesktop.DisplayManager");
841             g_variant_get (value, "ao", &iter);
842             while (g_variant_iter_loop (iter, "&o", &path))
843                 load_session (user_list, path);
844             g_variant_iter_free (iter);
845
846             g_variant_unref (value);
847         }
848         else
849             g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
850
851         g_variant_unref (result);
852     }
853 }
854
855 static void
856 load_users (CommonUserList *user_list)
857 {
858     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
859     GVariant *result;
860     GError *error = NULL;
861
862     if (priv->have_users)
863         return;
864     priv->have_users = TRUE;
865
866     /* Get user list from accounts service and fall back to /etc/passwd if that fails */
867     priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
868                                                                   "org.freedesktop.Accounts",
869                                                                   "org.freedesktop.Accounts",
870                                                                   "UserAdded",
871                                                                   "/org/freedesktop/Accounts",
872                                                                   NULL,
873                                                                   G_DBUS_SIGNAL_FLAGS_NONE,
874                                                                   accounts_user_added_cb,
875                                                                   user_list,
876                                                                   NULL);
877     priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
878                                                                     "org.freedesktop.Accounts",
879                                                                     "org.freedesktop.Accounts",
880                                                                     "UserDeleted",
881                                                                     "/org/freedesktop/Accounts",
882                                                                     NULL,
883                                                                     G_DBUS_SIGNAL_FLAGS_NONE,
884                                                                     accounts_user_deleted_cb,
885                                                                     user_list,
886                                                                     NULL);
887     result = g_dbus_connection_call_sync (priv->bus,
888                                           "org.freedesktop.Accounts",
889                                           "/org/freedesktop/Accounts",
890                                           "org.freedesktop.Accounts",
891                                           "ListCachedUsers",
892                                           g_variant_new ("()"),
893                                           G_VARIANT_TYPE ("(ao)"),
894                                           G_DBUS_CALL_FLAGS_NONE,
895                                           -1,
896                                           NULL,
897                                           &error);
898     if (error)
899         g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
900     g_clear_error (&error);
901     if (result)
902     {
903         GVariantIter *iter;
904         const gchar *path;
905
906         g_debug ("Loading users from org.freedesktop.Accounts");
907         g_variant_get (result, "(ao)", &iter);
908         while (g_variant_iter_loop (iter, "&o", &path))
909             add_accounts_user (user_list, path, FALSE);
910         g_variant_iter_free (iter);
911         g_variant_unref (result);
912     }
913     else
914     {
915         GFile *passwd_file;
916
917         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
918         priv->user_added_signal = 0;
919         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
920         priv->user_removed_signal = 0;
921
922         load_passwd_file (user_list, FALSE);
923
924         /* Watch for changes to user list */
925
926         passwd_file = g_file_new_for_path (PASSWD_FILE);
927         priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
928         g_object_unref (passwd_file);
929         if (error)
930             g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
931         else
932             g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
933         g_clear_error (&error);
934     }
935 }
936
937 /**
938  * common_user_list_get_length:
939  * @user_list: a #CommonUserList
940  *
941  * Return value: The number of users able to log in
942  **/
943 gint
944 common_user_list_get_length (CommonUserList *user_list)
945 {
946     g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
947     load_users (user_list);
948     return g_list_length (GET_LIST_PRIVATE (user_list)->users);
949 }
950
951 /**
952  * common_user_list_get_users:
953  * @user_list: A #CommonUserList
954  *
955  * Get a list of users to present to the user.  This list may be a subset of the
956  * available users and may be empty depending on the server configuration.
957  *
958  * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
959  **/
960 GList *
961 common_user_list_get_users (CommonUserList *user_list)
962 {
963     g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
964     load_users (user_list);
965     return GET_LIST_PRIVATE (user_list)->users;
966 }
967
968 /**
969  * common_user_list_get_user_by_name:
970  * @user_list: A #CommonUserList
971  * @username: Name of user to get.
972  *
973  * Get infomation about a given user or #NULL if this user doesn't exist.
974  * Includes hidden and system users, unlike the list from
975  * common_user_list_get_users.
976  *
977  * Return value: (transfer full): A #CommonUser entry for the given user.
978  **/
979 CommonUser *
980 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
981 {
982     g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
983     g_return_val_if_fail (username != NULL, NULL);
984
985     load_users (user_list);
986
987     CommonUser *user = get_user_by_name (user_list, username);
988     if (user)
989         return g_object_ref (user);
990
991     /* Sometimes we need to look up users that aren't in AccountsService.
992        Notably we need to look up the user that the greeter runs as, which
993        is usually 'lightdm'. For such cases, we manually create a one-off
994        CommonUser object and pre-seed with passwd info. */
995     struct passwd *entry = getpwnam (username);
996     if (entry != NULL)
997         return make_passwd_user (user_list, entry);
998
999     return NULL;
1000 }
1001
1002 static void
1003 common_user_list_init (CommonUserList *user_list)
1004 {
1005     CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1006
1007     priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1008 }
1009
1010 static void
1011 common_user_list_set_property (GObject    *object,
1012                                 guint       prop_id,
1013                                 const GValue *value,
1014                                 GParamSpec *pspec)
1015 {
1016     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1017 }
1018
1019 static void
1020 common_user_list_get_property (GObject    *object,
1021                                 guint       prop_id,
1022                                 GValue     *value,
1023                                 GParamSpec *pspec)
1024 {
1025     CommonUserList *self;
1026
1027     self = COMMON_USER_LIST (object);
1028
1029     switch (prop_id)
1030     {
1031     case LIST_PROP_NUM_USERS:
1032         g_value_set_int (value, common_user_list_get_length (self));
1033         break;
1034     default:
1035         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1036         break;
1037     }
1038 }
1039
1040 static void
1041 common_user_list_finalize (GObject *object)
1042 {
1043     CommonUserList *self = COMMON_USER_LIST (object);
1044     CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1045
1046     /* Remove children first, they might access us */
1047     g_list_free_full (priv->users, g_object_unref);
1048     g_list_free_full (priv->sessions, g_object_unref);
1049
1050     if (priv->user_added_signal)
1051         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1052     if (priv->user_removed_signal)
1053         g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1054     if (priv->session_added_signal)
1055         g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1056     if (priv->session_removed_signal)
1057         g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1058     g_object_unref (priv->bus);
1059     if (priv->passwd_monitor)
1060         g_object_unref (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-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-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-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 ("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 }