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