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