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