]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/user.c
Access GVariant values in a more type safe manner
[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             g_free (priv->name);
438             priv->name = g_variant_dup_string (value, NULL);
439         }
440         else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
441         {
442             g_free (priv->real_name);
443             priv->real_name = g_variant_dup_string (value, NULL);
444         }
445         else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
446         {
447             g_free (priv->home_directory);
448             priv->home_directory = g_variant_dup_string (value, NULL);
449         }
450         else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
451         {
452             g_free (priv->image);
453             priv->image = g_variant_dup_string (value, NULL);
454             if (strcmp (priv->image, "") == 0)
455             {
456                 g_free (priv->image);
457                 priv->image = NULL;
458             }
459         }
460         else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
461         {
462             g_free (priv->background);
463             priv->background = g_variant_dup_string (value, NULL);
464             if (strcmp (priv->background, "") == 0)
465             {
466                 g_free (priv->background);
467                 priv->background = NULL;
468             }
469         }
470         else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
471             priv->has_messages = g_variant_get_boolean (value);
472     }
473     g_variant_iter_free (iter);
474
475     g_variant_unref (result);
476
477     return TRUE;
478 }
479
480 static void
481 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
482 {
483     if (strcmp (signal_name, "Changed") == 0)
484     {
485         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
486         {
487             g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
488             update_user (object);
489             g_signal_emit (object->user, user_signals[CHANGED], 0);
490         }
491         else
492             g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
493     }
494 }
495
496 static UserAccountObject *
497 user_account_object_new (LightDMUserList *user_list, const gchar *path)
498 {
499     GDBusProxy *proxy;
500     UserAccountObject *object;
501     GError *error = NULL;
502
503     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
504                                            G_DBUS_PROXY_FLAGS_NONE,
505                                            NULL,
506                                            "org.freedesktop.Accounts",
507                                            path,
508                                            "org.freedesktop.Accounts.User",
509                                            NULL,
510                                            &error);
511     if (error)
512         g_warning ("Error getting user %s: %s", path, error->message);
513     g_clear_error (&error);
514     if (!proxy)
515         return NULL;
516
517     object = g_malloc0 (sizeof (UserAccountObject));  
518     object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
519     GET_USER_PRIVATE (object->user)->user_list = user_list;
520     object->proxy = proxy;
521     g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
522   
523     return object;
524 }
525
526 static void
527 user_account_object_free (UserAccountObject *object)
528 {
529     if (!object)
530         return;
531     g_object_unref (object->user);
532     g_object_unref (object->proxy);
533     g_free (object);
534 }
535
536 static UserAccountObject *
537 find_user_account_object (LightDMUserList *user_list, const gchar *path)
538 {
539     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
540     GList *link;
541
542     for (link = priv->user_account_objects; link; link = link->next)
543     {
544         UserAccountObject *object = link->data;
545         if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
546             return object;
547     }
548
549     return NULL;
550 }
551
552 static void
553 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
554 {
555     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
556   
557     if (strcmp (signal_name, "UserAdded") == 0)
558     {
559         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
560         {
561             gchar *path;
562             UserAccountObject *object;
563
564             g_variant_get (parameters, "(&o)", &path);
565
566             /* Ignore duplicate requests */
567             object = find_user_account_object (user_list, path);
568             if (object)
569                 return;
570
571             object = user_account_object_new (user_list, path);
572             if (object && update_user (object))
573             {
574                 g_debug ("User %s added", path);
575                 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
576                 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
577                 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
578                 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
579             }
580             else
581                 user_account_object_free (object);
582         }
583         else
584             g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
585     }
586     else if (strcmp (signal_name, "UserDeleted") == 0)
587     {
588         if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
589         {
590             gchar *path;
591             UserAccountObject *object;
592
593             g_variant_get (parameters, "(&o)", &path);
594
595             object = find_user_account_object (user_list, path);
596             if (!object)
597                 return;
598
599             g_debug ("User %s deleted", path);
600             priv->users = g_list_remove (priv->users, object->user);
601             g_object_unref (object->user);
602
603             g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
604
605             priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
606             user_account_object_free (object);
607         }
608         else
609             g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
610     }
611 }
612
613 static Session *
614 load_session (LightDMUserList *user_list, const gchar *path)
615 {
616     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
617     Session *session = NULL;
618     GVariant *result, *username;
619     GError *error = NULL;
620
621     result = g_dbus_connection_call_sync (priv->bus,
622                                           "org.freedesktop.DisplayManager",
623                                           path,
624                                           "org.freedesktop.DBus.Properties",
625                                           "Get",
626                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
627                                           G_VARIANT_TYPE ("(v)"),
628                                           G_DBUS_CALL_FLAGS_NONE,
629                                           -1,
630                                           NULL,
631                                           &error);
632     if (error)
633         g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
634     g_clear_error (&error);
635     if (!result)
636         return NULL;
637
638     g_variant_get (result, "(v)", &username);
639     if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
640     {
641         gchar *name;
642
643         g_variant_get (username, "&s", &name);
644
645         g_debug ("Loaded session %s (%s)", path, name);
646         session = g_object_new (session_get_type (), NULL);
647         session->username = g_strdup (name);
648         session->path = g_strdup (path);
649         priv->sessions = g_list_append (priv->sessions, session);
650     }
651     g_variant_unref (username);
652     g_variant_unref (result);
653
654     return session;
655 }
656
657 static void
658 session_added_cb (GDBusConnection *connection,
659                   const gchar *sender_name,
660                   const gchar *object_path,
661                   const gchar *interface_name,
662                   const gchar *signal_name,
663                   GVariant *parameters,
664                   gpointer data)
665 {
666     LightDMUserList *user_list = data;
667     gchar *path;
668     Session *session;
669     LightDMUser *user = NULL;
670
671     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
672     {
673         g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
674         return;
675     }
676
677     g_variant_get (parameters, "(&o)", &path);
678     session = load_session (user_list, path);
679     if (session)
680         user = get_user_by_name (user_list, session->username);
681     if (user)
682         g_signal_emit (user, user_signals[CHANGED], 0);
683 }
684
685 static void
686 session_removed_cb (GDBusConnection *connection,
687                     const gchar *sender_name,
688                     const gchar *object_path,
689                     const gchar *interface_name,
690                     const gchar *signal_name,
691                     GVariant *parameters,
692                     gpointer data)
693 {
694     LightDMUserList *user_list = data;
695     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
696     gchar *path;
697     GList *link;
698
699     if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
700     {
701         g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
702         return;
703     }
704
705     g_variant_get (parameters, "(&o)", &path);
706
707     for (link = priv->sessions; link; link = link->next)
708     {
709         Session *session = link->data;
710         if (strcmp (session->path, path) == 0)
711         {
712             LightDMUser *user;
713
714             g_debug ("Session %s removed", path);
715             priv->sessions = g_list_remove_link (priv->sessions, link);
716             user = get_user_by_name (user_list, session->username);
717             if (user)
718                 g_signal_emit (user, user_signals[CHANGED], 0);
719             g_object_unref (session);
720             break;
721         }
722     }
723 }
724
725 static void
726 update_users (LightDMUserList *user_list)
727 {
728     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
729     GVariant *result;
730     GError *error = NULL;
731
732     if (priv->have_users)
733         return;
734     priv->have_users = TRUE;
735
736     priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
737                                                                   G_DBUS_PROXY_FLAGS_NONE,
738                                                                   NULL,
739                                                                   "org.freedesktop.Accounts",
740                                                                   "/org/freedesktop/Accounts",
741                                                                   "org.freedesktop.Accounts",
742                                                                   NULL,
743                                                                   &error);
744     if (error)
745         g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
746     g_clear_error (&error);
747
748     /* Check if the service exists */
749     if (priv->accounts_service_proxy)
750     {
751         gchar *name;
752
753         name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
754         if (!name)
755         {
756             g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
757             g_object_unref (priv->accounts_service_proxy);
758             priv->accounts_service_proxy = NULL;
759         }
760         g_free (name);
761     }
762
763     if (priv->accounts_service_proxy)
764     {
765         GVariant *result;
766
767         g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
768
769         result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
770                                          "ListCachedUsers",
771                                          g_variant_new ("()"),
772                                          G_DBUS_CALL_FLAGS_NONE,
773                                          -1,
774                                          NULL,
775                                          &error);
776         if (error)
777             g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
778         g_clear_error (&error);
779         if (!result)
780             return;
781
782         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
783         {
784             GVariantIter *iter;
785             const gchar *path;
786
787             g_debug ("Loading users from org.freedesktop.Accounts");
788             g_variant_get (result, "(ao)", &iter);
789             while (g_variant_iter_loop (iter, "&o", &path))
790             {
791                 UserAccountObject *object;
792
793                 g_debug ("Loading user %s", path);
794
795                 object = user_account_object_new (user_list, path);
796                 if (object && update_user (object))
797                 {
798                     priv->user_account_objects = g_list_append (priv->user_account_objects, object);
799                     priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
800                     g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
801                 }
802                 else
803                     user_account_object_free (object);
804             }
805             g_variant_iter_free (iter);
806         }
807         else
808             g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
809
810         g_variant_unref (result);
811     }
812     else
813     {
814         GFile *passwd_file;
815
816         load_passwd_file (user_list, FALSE);
817
818         /* Watch for changes to user list */
819
820         passwd_file = g_file_new_for_path (PASSWD_FILE);
821         priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
822         g_object_unref (passwd_file);
823         if (error)
824             g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
825         else
826             g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
827         g_clear_error (&error);
828     }
829
830     priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
831                                                                      "org.freedesktop.DisplayManager",
832                                                                      "org.freedesktop.DisplayManager",
833                                                                      "SessionAdded",
834                                                                      "/org/freedesktop/DisplayManager",
835                                                                      NULL,
836                                                                      G_DBUS_SIGNAL_FLAGS_NONE,
837                                                                      session_added_cb,
838                                                                      user_list,
839                                                                      NULL);
840     priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
841                                                                        "org.freedesktop.DisplayManager",
842                                                                        "org.freedesktop.DisplayManager",
843                                                                        "SessionRemoved",
844                                                                        "/org/freedesktop/DisplayManager",
845                                                                        NULL,
846                                                                        G_DBUS_SIGNAL_FLAGS_NONE,
847                                                                        session_removed_cb,
848                                                                        user_list,
849                                                                        NULL);
850     result = g_dbus_connection_call_sync (priv->bus,
851                                           "org.freedesktop.DisplayManager",
852                                           "/org/freedesktop/DisplayManager",
853                                           "org.freedesktop.DBus.Properties",
854                                           "Get",
855                                           g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
856                                           G_VARIANT_TYPE ("(v)"),
857                                           G_DBUS_CALL_FLAGS_NONE,
858                                           -1,
859                                           NULL,
860                                           &error);
861     if (error)
862         g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
863     g_clear_error (&error);
864     if (result)
865     {
866         if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
867         {
868             GVariant *value;
869             GVariantIter *iter;
870             const gchar *path;
871
872             g_variant_get (result, "(v)", &value);
873
874             g_debug ("Loading sessions from org.freedesktop.DisplayManager");
875             g_variant_get (value, "ao", &iter);
876             while (g_variant_iter_loop (iter, "&o", &path))
877                 load_session (user_list, path);
878             g_variant_iter_free (iter);
879
880             g_variant_unref (value);
881         }
882         else
883             g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
884
885         g_variant_unref (result);
886     }
887 }
888
889 /**
890  * lightdm_user_list_get_length:
891  * @user_list: a #LightDMUserList
892  *
893  * Return value: The number of users able to log in
894  **/
895 gint
896 lightdm_user_list_get_length (LightDMUserList *user_list)
897 {
898     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
899     update_users (user_list);
900     return g_list_length (GET_LIST_PRIVATE (user_list)->users);
901 }
902
903 /**
904  * lightdm_user_list_get_users:
905  * @user_list: A #LightDMUserList
906  *
907  * Get a list of users to present to the user.  This list may be a subset of the
908  * available users and may be empty depending on the server configuration.
909  *
910  * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
911  **/
912 GList *
913 lightdm_user_list_get_users (LightDMUserList *user_list)
914 {
915     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
916     update_users (user_list);
917     return GET_LIST_PRIVATE (user_list)->users;
918 }
919
920 /**
921  * lightdm_user_list_get_user_by_name:
922  * @user_list: A #LightDMUserList
923  * @username: Name of user to get.
924  *
925  * Get infomation about a given user or #NULL if this user doesn't exist.
926  *
927  * Return value: (transfer none): A #LightDMUser entry for the given user.
928  **/
929 LightDMUser *
930 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
931 {
932     g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
933     g_return_val_if_fail (username != NULL, NULL);
934
935     update_users (user_list);
936
937     return get_user_by_name (user_list, username);
938 }
939
940 static void
941 lightdm_user_list_init (LightDMUserList *user_list)
942 {
943     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
944
945     priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
946 }
947
948 static void
949 lightdm_user_list_set_property (GObject    *object,
950                                 guint       prop_id,
951                                 const GValue *value,
952                                 GParamSpec *pspec)
953 {
954     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
955 }
956
957 static void
958 lightdm_user_list_get_property (GObject    *object,
959                                 guint       prop_id,
960                                 GValue     *value,
961                                 GParamSpec *pspec)
962 {
963     LightDMUserList *self;
964
965     self = LIGHTDM_USER_LIST (object);
966
967     switch (prop_id)
968     {
969     case LIST_PROP_NUM_USERS:
970         g_value_set_int (value, lightdm_user_list_get_length (self));
971         break;
972     default:
973         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
974         break;
975     }
976 }
977
978 static void
979 lightdm_user_list_finalize (GObject *object)
980 {
981     LightDMUserList *self = LIGHTDM_USER_LIST (object);
982     LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
983
984     if (priv->accounts_service_proxy)
985         g_object_unref (priv->accounts_service_proxy);
986     g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
987     if (priv->passwd_monitor)
988         g_object_unref (priv->passwd_monitor);
989     g_list_free_full (priv->users, g_object_unref);
990     g_list_free_full (priv->sessions, g_object_unref);
991     if (priv->session_added_signal)
992         g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
993     if (priv->session_removed_signal)
994         g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
995     g_object_unref (priv->bus);
996
997     G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
998 }
999
1000 static void
1001 lightdm_user_list_class_init (LightDMUserListClass *klass)
1002 {
1003     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1004
1005     g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
1006
1007     object_class->set_property = lightdm_user_list_set_property;
1008     object_class->get_property = lightdm_user_list_get_property;
1009     object_class->finalize = lightdm_user_list_finalize;
1010
1011     g_object_class_install_property (object_class,
1012                                      LIST_PROP_NUM_USERS,
1013                                      g_param_spec_int ("num-users",
1014                                                        "num-users",
1015                                                        "Number of login users",
1016                                                        0, G_MAXINT, 0,
1017                                                        G_PARAM_READABLE));
1018     /**
1019      * LightDMUserList::user-added:
1020      * @user_list: A #LightDMUserList
1021      * @user: The #LightDM user that has been added.
1022      *
1023      * The ::user-added signal gets emitted when a user account is created.
1024      **/
1025     list_signals[USER_ADDED] =
1026         g_signal_new ("user-added",
1027                       G_TYPE_FROM_CLASS (klass),
1028                       G_SIGNAL_RUN_LAST,
1029                       G_STRUCT_OFFSET (LightDMUserListClass, user_added),
1030                       NULL, NULL,
1031                       NULL,
1032                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1033
1034     /**
1035      * LightDMUserList::user-changed:
1036      * @user_list: A #LightDMUserList
1037      * @user: The #LightDM user that has been changed.
1038      *
1039      * The ::user-changed signal gets emitted when a user account is modified.
1040      **/
1041     list_signals[USER_CHANGED] =
1042         g_signal_new ("user-changed",
1043                       G_TYPE_FROM_CLASS (klass),
1044                       G_SIGNAL_RUN_LAST,
1045                       G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
1046                       NULL, NULL,
1047                       NULL,
1048                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1049
1050     /**
1051      * LightDMUserList::user-removed:
1052      * @user_list: A #LightDMUserList
1053      * @user: The #LightDM user that has been removed.
1054      *
1055      * The ::user-removed signal gets emitted when a user account is removed.
1056      **/
1057     list_signals[USER_REMOVED] =
1058         g_signal_new ("user-removed",
1059                       G_TYPE_FROM_CLASS (klass),
1060                       G_SIGNAL_RUN_LAST,
1061                       G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1062                       NULL, NULL,
1063                       NULL,
1064                       G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1065 }
1066
1067 /**
1068  * lightdm_user_get_name:
1069  * @user: A #LightDMUser
1070  * 
1071  * Get the name of a user.
1072  * 
1073  * Return value: The name of the given user
1074  **/
1075 const gchar *
1076 lightdm_user_get_name (LightDMUser *user)
1077 {
1078     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1079     return GET_USER_PRIVATE (user)->name;
1080 }
1081
1082 /**
1083  * lightdm_user_get_real_name:
1084  * @user: A #LightDMUser
1085  * 
1086  * Get the real name of a user.
1087  *
1088  * Return value: The real name of the given user
1089  **/
1090 const gchar *
1091 lightdm_user_get_real_name (LightDMUser *user)
1092 {
1093     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1094     return GET_USER_PRIVATE (user)->real_name;
1095 }
1096
1097 /**
1098  * lightdm_user_get_display_name:
1099  * @user: A #LightDMUser
1100  * 
1101  * Get the display name of a user.
1102  * 
1103  * Return value: The display name of the given user
1104  **/
1105 const gchar *
1106 lightdm_user_get_display_name (LightDMUser *user)
1107 {
1108     LightDMUserPrivate *priv;
1109
1110     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1111
1112     priv = GET_USER_PRIVATE (user);
1113     if (strcmp (priv->real_name, ""))
1114         return priv->real_name;
1115     else
1116         return priv->name;
1117 }
1118
1119 /**
1120  * lightdm_user_get_home_directory:
1121  * @user: A #LightDMUser
1122  * 
1123  * Get the home directory for a user.
1124  * 
1125  * Return value: The users home directory
1126  */
1127 const gchar *
1128 lightdm_user_get_home_directory (LightDMUser *user)
1129 {
1130     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1131     return GET_USER_PRIVATE (user)->home_directory;
1132 }
1133
1134 /**
1135  * lightdm_user_get_image:
1136  * @user: A #LightDMUser
1137  * 
1138  * Get the image URI for a user.
1139  * 
1140  * Return value: The image URI for the given user or #NULL if no URI
1141  **/
1142 const gchar *
1143 lightdm_user_get_image (LightDMUser *user)
1144 {
1145     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1146     return GET_USER_PRIVATE (user)->image;
1147 }
1148
1149 /**
1150  * lightdm_user_get_background:
1151  * @user: A #LightDMUser
1152  * 
1153  * Get the background file path for a user.
1154  * 
1155  * Return value: The background file path for the given user or #NULL if no path
1156  **/
1157 const gchar *
1158 lightdm_user_get_background (LightDMUser *user)
1159 {
1160     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1161     return GET_USER_PRIVATE (user)->background;
1162 }
1163
1164 static void
1165 load_dmrc (LightDMUser *user)
1166 {
1167     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1168     gchar *path;
1169     //gboolean have_dmrc;
1170
1171     if (!priv->dmrc_file)
1172         priv->dmrc_file = g_key_file_new ();
1173
1174     /* Load from the user directory */  
1175     path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1176     /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1177     g_free (path);
1178
1179     /* If no ~/.dmrc, then load from the cache */
1180     // FIXME
1181
1182     // FIXME: Watch for changes
1183
1184     /* The Language field contains the locale */
1185     if (priv->language)
1186         g_free (priv->language);
1187     priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1188
1189     if (priv->layouts)
1190     {
1191         g_strfreev (priv->layouts);
1192         priv->layouts = NULL;
1193     }
1194     if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1195     {
1196         priv->layouts = g_malloc (sizeof (gchar *) * 2);
1197         priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1198         priv->layouts[1] = NULL;
1199     }
1200
1201     if (priv->session)
1202         g_free (priv->session);
1203     priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1204 }
1205
1206 static GVariant *
1207 get_property (GDBusProxy *proxy, const gchar *property)
1208 {
1209     GVariant *answer;
1210
1211     if (!proxy)
1212         return NULL;
1213
1214     answer = g_dbus_proxy_get_cached_property (proxy, property);
1215
1216     if (!answer)
1217     {
1218         g_warning ("Could not get accounts property %s", property);
1219         return NULL;
1220     }
1221
1222     return answer;
1223 }
1224
1225 static gboolean
1226 get_boolean_property (GDBusProxy *proxy, const gchar *property)
1227 {
1228     GVariant *answer;
1229     gboolean rv;
1230
1231     answer = get_property (proxy, property);
1232     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN))
1233     {
1234         g_warning ("Unexpected accounts property type for %s: %s",
1235                    property, g_variant_get_type_string (answer));
1236         g_variant_unref (answer);
1237         return FALSE;
1238     }
1239
1240     rv = g_variant_get_boolean (answer);
1241     g_variant_unref (answer);
1242
1243     return rv;
1244 }
1245
1246 static gchar *
1247 get_string_property (GDBusProxy *proxy, const gchar *property)
1248 {
1249     GVariant *answer;
1250     gchar *rv;
1251   
1252     answer = get_property (proxy, property);
1253     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING))
1254     {
1255         g_warning ("Unexpected accounts property type for %s: %s",
1256                    property, g_variant_get_type_string (answer));
1257         g_variant_unref (answer);
1258         return NULL;
1259     }
1260
1261     rv = g_strdup (g_variant_get_string (answer, NULL));
1262     if (strcmp (rv, "") == 0)
1263     {
1264         g_free (rv);
1265         rv = NULL;
1266     }
1267     g_variant_unref (answer);
1268
1269     return rv;
1270 }
1271
1272 static gchar **
1273 get_string_array_property (GDBusProxy *proxy, const gchar *property)
1274 {
1275     GVariant *answer;
1276     gchar **rv;
1277
1278     if (!proxy)
1279         return NULL;
1280
1281     answer = g_dbus_proxy_get_cached_property (proxy, property);
1282
1283     if (!answer)
1284     {
1285         g_warning ("Could not get accounts property %s", property);
1286         return NULL;
1287     }
1288
1289     if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
1290     {
1291         g_warning ("Unexpected accounts property type for %s: %s",
1292                    property, g_variant_get_type_string (answer));
1293         g_variant_unref (answer);
1294         return NULL;
1295     }
1296
1297     rv = g_variant_dup_strv (answer, NULL);
1298
1299     g_variant_unref (answer);
1300     return rv;
1301 }
1302
1303 static gboolean
1304 load_accounts_service (LightDMUser *user)
1305 {
1306     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1307     LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1308     UserAccountObject *account = NULL;
1309     GList *iter;
1310     gchar **value;
1311
1312     /* First, find AccountObject proxy */
1313     for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1314     {
1315         UserAccountObject *a = iter->data;
1316         if (a->user == user)
1317         {
1318             account = a;
1319             break;
1320         }
1321     }
1322     if (!account)
1323         return FALSE;
1324
1325     /* We have proxy, let's grab some properties */
1326     if (priv->language)
1327         g_free (priv->language);
1328     priv->language = get_string_property (account->proxy, "Language");
1329     if (priv->session)
1330         g_free (priv->session);
1331     priv->session = get_string_property (account->proxy, "XSession");
1332
1333     value = get_string_array_property (account->proxy, "XKeyboardLayouts");
1334     if (value)
1335     {
1336         if (value[0])
1337         {
1338             g_strfreev (priv->layouts);
1339             priv->layouts = value;
1340         }
1341         else
1342             g_strfreev (value);
1343     }
1344
1345     priv->has_messages = get_boolean_property (account->proxy, "XHasMessages");
1346
1347     return TRUE;
1348 }
1349
1350 /* Loads language/layout/session info for user */
1351 static void
1352 load_user_values (LightDMUser *user)
1353 {
1354     LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1355
1356     load_dmrc (user);
1357     load_accounts_service (user); // overrides dmrc values
1358
1359     /* Ensure a few guarantees */
1360     if (priv->layouts == NULL)
1361     {
1362         priv->layouts = g_malloc (sizeof (gchar *) * 1);
1363         priv->layouts[0] = NULL;
1364     }
1365 }
1366
1367 /**
1368  * lightdm_user_get_language:
1369  * @user: A #LightDMUser
1370  * 
1371  * Get the language for a user.
1372  * 
1373  * 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.
1374  **/
1375 const gchar *
1376 lightdm_user_get_language (LightDMUser *user)
1377 {
1378     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1379     load_user_values (user);
1380     return GET_USER_PRIVATE (user)->language;
1381 }
1382
1383 /**
1384  * lightdm_user_get_layout:
1385  * @user: A #LightDMUser
1386  * 
1387  * Get the keyboard layout for a user.
1388  * 
1389  * 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.
1390  **/
1391 const gchar *
1392 lightdm_user_get_layout (LightDMUser *user)
1393 {
1394     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1395     load_user_values (user);
1396     return GET_USER_PRIVATE (user)->layouts[0];
1397 }
1398
1399 /**
1400  * lightdm_user_get_layouts:
1401  * @user: A #LightDMUser
1402  * 
1403  * Get the configured keyboard layouts for a user.
1404  * 
1405  * 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.
1406  **/
1407 const gchar * const *
1408 lightdm_user_get_layouts (LightDMUser *user)
1409 {
1410     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1411     load_user_values (user);
1412     return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1413 }
1414
1415 /**
1416  * lightdm_user_get_session:
1417  * @user: A #LightDMUser
1418  * 
1419  * Get the session for a user.
1420  * 
1421  * Return value: The session for the given user or #NULL if using system defaults.
1422  **/
1423 const gchar *
1424 lightdm_user_get_session (LightDMUser *user)
1425 {
1426     g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1427     load_user_values (user);
1428     return GET_USER_PRIVATE (user)->session; 
1429 }
1430
1431 /**
1432  * lightdm_user_get_logged_in:
1433  * @user: A #LightDMUser
1434  * 
1435  * Check if a user is logged in.
1436  * 
1437  * Return value: #TRUE if the user is currently logged in.
1438  **/
1439 gboolean
1440 lightdm_user_get_logged_in (LightDMUser *user)
1441 {
1442     LightDMUserPrivate *priv;
1443     LightDMUserListPrivate *list_priv;
1444     GList *link;
1445
1446     g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1447
1448     priv = GET_USER_PRIVATE (user);
1449     list_priv = GET_LIST_PRIVATE (priv->user_list);
1450
1451     for (link = list_priv->sessions; link; link = link->next)
1452     {
1453         Session *session = link->data;
1454         if (strcmp (session->username, priv->name) == 0)
1455             return TRUE;
1456     }
1457
1458     return FALSE;
1459 }
1460
1461 /**
1462  * lightdm_user_get_has_messages:
1463  * @user: A #LightDMUser
1464  * 
1465  * Check if a user has waiting messages.
1466  * 
1467  * Return value: #TRUE if the user has waiting messages.
1468  **/
1469 gboolean
1470 lightdm_user_get_has_messages (LightDMUser *user)
1471 {
1472     g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1473     load_user_values (user);
1474     return GET_USER_PRIVATE (user)->has_messages;
1475 }
1476
1477 static void
1478 lightdm_user_init (LightDMUser *user)
1479 {
1480 }
1481
1482 static void
1483 lightdm_user_set_property (GObject    *object,
1484                            guint       prop_id,
1485                            const GValue *value,
1486                            GParamSpec *pspec)
1487 {
1488     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1489 }
1490
1491 static void
1492 lightdm_user_get_property (GObject    *object,
1493                            guint       prop_id,
1494                            GValue     *value,
1495                            GParamSpec *pspec)
1496 {
1497     LightDMUser *self;
1498
1499     self = LIGHTDM_USER (object);
1500
1501     switch (prop_id)
1502     {
1503     case USER_PROP_NAME:
1504         g_value_set_string (value, lightdm_user_get_name (self));
1505         break;
1506     case USER_PROP_REAL_NAME:
1507         g_value_set_string (value, lightdm_user_get_real_name (self));
1508         break;
1509     case USER_PROP_DISPLAY_NAME:
1510         g_value_set_string (value, lightdm_user_get_display_name (self));
1511         break;
1512     case USER_PROP_HOME_DIRECTORY:
1513         g_value_set_string (value, lightdm_user_get_home_directory (self));
1514         break;
1515     case USER_PROP_IMAGE:
1516         g_value_set_string (value, lightdm_user_get_image (self));
1517         break;
1518     case USER_PROP_BACKGROUND:
1519         g_value_set_string (value, lightdm_user_get_background (self));
1520         break;
1521     case USER_PROP_LANGUAGE:
1522         g_value_set_string (value, lightdm_user_get_language (self));
1523         break;
1524     case USER_PROP_LAYOUT:
1525         g_value_set_string (value, lightdm_user_get_layout (self));
1526         break;
1527     case USER_PROP_LAYOUTS:
1528         g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
1529         break;
1530     case USER_PROP_SESSION:
1531         g_value_set_string (value, lightdm_user_get_session (self));
1532         break;
1533     case USER_PROP_LOGGED_IN:
1534         g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1535         break;
1536     case USER_PROP_HAS_MESSAGES:
1537         g_value_set_boolean (value, lightdm_user_get_has_messages (self));
1538         break;
1539     default:
1540         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1541         break;
1542     }
1543 }
1544
1545 static void
1546 lightdm_user_finalize (GObject *object)
1547 {
1548     LightDMUser *self = LIGHTDM_USER (object);
1549     LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1550
1551     g_free (priv->name);
1552     g_free (priv->real_name);
1553     g_free (priv->home_directory);
1554     g_free (priv->image);
1555     g_free (priv->background);
1556     g_strfreev (priv->layouts);
1557     if (priv->dmrc_file)
1558         g_key_file_free (priv->dmrc_file);
1559 }
1560
1561 static void
1562 lightdm_user_class_init (LightDMUserClass *klass)
1563 {
1564     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1565   
1566     g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1567
1568     object_class->set_property = lightdm_user_set_property;
1569     object_class->get_property = lightdm_user_get_property;
1570     object_class->finalize = lightdm_user_finalize;
1571
1572     g_object_class_install_property (object_class,
1573                                      USER_PROP_NAME,
1574                                      g_param_spec_string ("name",
1575                                                           "name",
1576                                                           "Username",
1577                                                           NULL,
1578                                                           G_PARAM_READWRITE));
1579     g_object_class_install_property (object_class,
1580                                      USER_PROP_REAL_NAME,
1581                                      g_param_spec_string ("real-name",
1582                                                           "real-name",
1583                                                           "Users real name",
1584                                                           NULL,
1585                                                           G_PARAM_READWRITE));
1586     g_object_class_install_property (object_class,
1587                                      USER_PROP_DISPLAY_NAME,
1588                                      g_param_spec_string ("display-name",
1589                                                           "display-name",
1590                                                           "Users display name",
1591                                                           NULL,
1592                                                           G_PARAM_READABLE));
1593     g_object_class_install_property (object_class,
1594                                      USER_PROP_HOME_DIRECTORY,
1595                                      g_param_spec_string ("home-directory",
1596                                                           "home-directory",
1597                                                           "Home directory",
1598                                                           NULL,
1599                                                           G_PARAM_READWRITE));
1600     g_object_class_install_property (object_class,
1601                                      USER_PROP_IMAGE,
1602                                      g_param_spec_string ("image",
1603                                                           "image",
1604                                                           "Avatar image",
1605                                                           NULL,
1606                                                           G_PARAM_READWRITE));
1607     g_object_class_install_property (object_class,
1608                                      USER_PROP_BACKGROUND,
1609                                      g_param_spec_string ("background",
1610                                                           "background",
1611                                                           "User background",
1612                                                           NULL,
1613                                                           G_PARAM_READWRITE));
1614     g_object_class_install_property (object_class,
1615                                      USER_PROP_LANGUAGE,
1616                                      g_param_spec_string ("language",
1617                                                          "language",
1618                                                          "Language used by this user",
1619                                                          NULL,
1620                                                          G_PARAM_READABLE));
1621     g_object_class_install_property (object_class,
1622                                      USER_PROP_LAYOUT,
1623                                      g_param_spec_string ("layout",
1624                                                           "layout",
1625                                                           "Keyboard layout used by this user",
1626                                                           NULL,
1627                                                           G_PARAM_READABLE));
1628     g_object_class_install_property (object_class,
1629                                      USER_PROP_LAYOUTS,
1630                                      g_param_spec_boxed ("layouts",
1631                                                          "layouts",
1632                                                          "Keyboard layouts used by this user",
1633                                                          G_TYPE_STRV,
1634                                                          G_PARAM_READABLE));
1635     g_object_class_install_property (object_class,
1636                                      USER_PROP_SESSION,
1637                                      g_param_spec_string ("session",
1638                                                           "session",
1639                                                           "Session used by this user",
1640                                                           NULL,
1641                                                           G_PARAM_READABLE));
1642     g_object_class_install_property (object_class,
1643                                      USER_PROP_LOGGED_IN,
1644                                      g_param_spec_boolean ("logged-in",
1645                                                            "logged-in",
1646                                                            "TRUE if the user is currently in a session",
1647                                                            FALSE,
1648                                                            G_PARAM_READWRITE));
1649     g_object_class_install_property (object_class,
1650                                      USER_PROP_LOGGED_IN,
1651                                      g_param_spec_boolean ("has-messages",
1652                                                            "has-messages",
1653                                                            "TRUE if the user is has waiting messages",
1654                                                            FALSE,
1655                                                            G_PARAM_READWRITE));
1656
1657     /**
1658      * LightDMUser::changed:
1659      * @user: A #LightDMUser
1660      *
1661      * The ::changed signal gets emitted this user account is modified.
1662      **/
1663     user_signals[CHANGED] =
1664         g_signal_new ("changed",
1665                       G_TYPE_FROM_CLASS (klass),
1666                       G_SIGNAL_RUN_LAST,
1667                       G_STRUCT_OFFSET (LightDMUserClass, changed),
1668                       NULL, NULL,
1669                       NULL,
1670                       G_TYPE_NONE, 0);
1671 }
1672
1673 static void
1674 session_init (Session *session)
1675 {
1676 }
1677
1678 static void
1679 session_finalize (GObject *object)
1680 {
1681     Session *self = SESSION (object);
1682
1683     g_free (self->path);
1684     g_free (self->username);
1685 }
1686
1687 static void
1688 session_class_init (SessionClass *klass)
1689 {
1690     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1691     object_class->finalize = session_finalize;
1692 }