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