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