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