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