2 * Copyright (C) 2010 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; either version 3 of the License, or (at your option) any
8 * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
16 #include <sys/utsname.h>
20 #include "lightdm/user.h"
34 USER_PROP_DISPLAY_NAME,
35 USER_PROP_HOME_DIRECTORY,
51 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
58 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
62 /* Connection to AccountsService */
63 GDBusProxy *accounts_service_proxy;
64 GList *user_account_objects;
66 /* Connection to DisplayManager */
67 GDBusProxy *display_manager_proxy;
69 /* File monitor for password file */
70 GFileMonitor *passwd_monitor;
72 /* TRUE if have scanned users */
78 /* List of sessions */
80 } LightDMUserListPrivate;
90 LightDMUserList *user_list;
94 gchar *home_directory;
102 } LightDMUserPrivate;
106 GObject parent_instance;
113 GObjectClass parent_class;
116 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
117 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
118 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
119 GType session_get_type (void);
120 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
122 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
123 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
125 #define PASSWD_FILE "/etc/passwd"
126 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
128 static LightDMUserList *singleton = NULL;
131 * lightdm_user_list_get_instance:
135 * Return value: (tranfer none): the #LightDMUserList
138 lightdm_user_list_get_instance (void)
141 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
146 get_user_by_name (LightDMUserList *user_list, const gchar *username)
148 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
151 for (link = priv->users; link; link = link->next)
153 LightDMUser *user = link->data;
154 if (strcmp (lightdm_user_get_name (user), username) == 0)
162 compare_user (gconstpointer a, gconstpointer b)
164 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
165 return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
169 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
171 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
173 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
174 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
175 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
178 g_free (priv->real_name);
179 priv->real_name = g_strdup (real_name);
180 g_free (priv->home_directory);
181 priv->home_directory = g_strdup (home_directory);
182 g_free (priv->image);
183 priv->image = g_strdup (image);
189 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
191 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
195 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
197 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
201 gchar **hidden_users, **hidden_shells;
202 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
203 GError *error = NULL;
205 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
207 config = g_key_file_new ();
208 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
209 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
210 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
211 g_clear_error (&error);
213 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
214 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
218 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
220 value = g_strdup ("nobody nobody4 noaccess");
221 hidden_users = g_strsplit (value, " ", -1);
224 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
226 value = g_strdup ("/bin/false /usr/sbin/nologin");
227 hidden_shells = g_strsplit (value, " ", -1);
230 g_key_file_free (config);
236 struct passwd *entry;
238 LightDMUserPrivate *user_priv;
240 gchar *real_name, *image;
248 /* Ignore system users */
249 if (entry->pw_uid < minimum_uid)
252 /* Ignore users disabled by shell */
255 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
256 if (hidden_shells[i])
260 /* Ignore certain users */
261 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
265 tokens = g_strsplit (entry->pw_gecos, ",", -1);
266 if (tokens[0] != NULL && tokens[0][0] != '\0')
267 real_name = g_strdup (tokens[0]);
269 real_name = g_strdup ("");
272 image = g_build_filename (entry->pw_dir, ".face", NULL);
273 if (!g_file_test (image, G_FILE_TEST_EXISTS))
276 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
277 if (!g_file_test (image, G_FILE_TEST_EXISTS))
284 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
285 user_priv = GET_USER_PRIVATE (user);
286 user_priv->user_list = user_list;
287 g_free (user_priv->name);
288 user_priv->name = g_strdup (entry->pw_name);
289 g_free (user_priv->real_name);
290 user_priv->real_name = real_name;
291 g_free (user_priv->home_directory);
292 user_priv->home_directory = g_strdup (entry->pw_dir);
293 g_free (user_priv->image);
294 user_priv->image = image;
296 /* Update existing users if have them */
297 for (link = priv->users; link; link = link->next)
299 LightDMUser *info = link->data;
300 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
302 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
303 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
304 g_object_unref (user);
311 /* Only notify once we have loaded the user list */
312 if (priv->have_users)
313 new_users = g_list_insert_sorted (new_users, user, compare_user);
315 users = g_list_insert_sorted (users, user, compare_user);
317 g_strfreev (hidden_users);
318 g_strfreev (hidden_shells);
321 g_warning ("Failed to read password database: %s", strerror (errno));
325 /* Use new user list */
326 old_users = priv->users;
329 /* Notify of changes */
330 for (link = new_users; link; link = link->next)
332 LightDMUser *info = link->data;
333 g_debug ("User %s added", lightdm_user_get_name (info));
334 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
336 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
338 g_list_free (new_users);
339 for (link = changed_users; link; link = link->next)
341 LightDMUser *info = link->data;
342 g_debug ("User %s changed", lightdm_user_get_name (info));
343 g_signal_emit (info, user_signals[CHANGED], 0);
345 g_list_free (changed_users);
346 for (link = old_users; link; link = link->next)
350 /* See if this user is in the current list */
351 for (new_link = priv->users; new_link; new_link = new_link->next)
353 if (new_link->data == link->data)
359 LightDMUser *info = link->data;
360 g_debug ("User %s removed", lightdm_user_get_name (info));
361 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
362 g_object_unref (info);
365 g_list_free (old_users);
369 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
371 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
373 g_debug ("%s changed, reloading user list", g_file_get_path (file));
374 load_passwd_file (user_list, TRUE);
379 update_user (UserAccountObject *object)
381 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
382 GVariant *result, *value;
385 GError *error = NULL;
387 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
388 "org.freedesktop.Accounts",
389 g_dbus_proxy_get_object_path (object->proxy),
390 "org.freedesktop.DBus.Properties",
392 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
393 G_VARIANT_TYPE ("(a{sv})"),
394 G_DBUS_CALL_FLAGS_NONE,
399 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
400 g_clear_error (&error);
404 g_variant_get (result, "(a{sv})", &iter);
405 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
407 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
410 g_variant_get (value, "&s", &user_name);
412 priv->name = g_strdup (user_name);
414 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
417 g_variant_get (value, "&s", &real_name);
418 g_free (priv->real_name);
419 priv->real_name = g_strdup (real_name);
421 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
423 gchar *home_directory;
424 g_variant_get (value, "&s", &home_directory);
425 g_free (priv->home_directory);
426 priv->home_directory = g_strdup (home_directory);
428 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
431 g_variant_get (value, "&s", &icon_file);
432 g_free (priv->image);
433 if (strcmp (icon_file, "") == 0)
436 priv->image = g_strdup (icon_file);
438 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
440 gchar *background_file;
441 g_variant_get (value, "&s", &background_file);
442 g_free (priv->background);
443 if (strcmp (background_file, "") == 0)
444 priv->background = NULL;
446 priv->background = g_strdup (background_file);
449 g_variant_iter_free (iter);
451 g_variant_unref (result);
457 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
459 if (strcmp (signal_name, "Changed") == 0)
461 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
463 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
464 update_user (object);
465 g_signal_emit (object->user, user_signals[CHANGED], 0);
468 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
472 static UserAccountObject *
473 user_account_object_new (LightDMUserList *user_list, const gchar *path)
476 UserAccountObject *object;
477 GError *error = NULL;
479 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
480 G_DBUS_PROXY_FLAGS_NONE,
482 "org.freedesktop.Accounts",
484 "org.freedesktop.Accounts.User",
488 g_warning ("Error getting user %s: %s", path, error->message);
489 g_clear_error (&error);
493 object = g_malloc0 (sizeof (UserAccountObject));
494 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
495 GET_USER_PRIVATE (object->user)->user_list = user_list;
496 object->proxy = proxy;
497 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
503 user_account_object_free (UserAccountObject *object)
507 g_object_unref (object->user);
508 g_object_unref (object->proxy);
512 static UserAccountObject *
513 find_user_account_object (LightDMUserList *user_list, const gchar *path)
515 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
518 for (link = priv->user_account_objects; link; link = link->next)
520 UserAccountObject *object = link->data;
521 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
529 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
531 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
533 if (strcmp (signal_name, "UserAdded") == 0)
535 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
538 UserAccountObject *object;
540 g_variant_get (parameters, "(&o)", &path);
542 /* Ignore duplicate requests */
543 object = find_user_account_object (user_list, path);
547 object = user_account_object_new (user_list, path);
548 if (object && update_user (object))
550 g_debug ("User %s added", path);
551 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
552 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
553 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
554 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
557 user_account_object_free (object);
560 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
562 else if (strcmp (signal_name, "UserDeleted") == 0)
564 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
567 UserAccountObject *object;
569 g_variant_get (parameters, "(&o)", &path);
571 object = find_user_account_object (user_list, path);
575 g_debug ("User %s deleted", path);
576 priv->users = g_list_remove (priv->users, object->user);
577 g_object_unref (object->user);
579 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
581 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
582 user_account_object_free (object);
585 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
590 load_session (LightDMUserList *user_list, const gchar *path)
592 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
593 Session *session = NULL;
594 GVariant *result, *username;
595 GError *error = NULL;
597 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
598 "org.freedesktop.DisplayManager",
600 "org.freedesktop.DBus.Properties",
602 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
603 G_VARIANT_TYPE ("(v)"),
604 G_DBUS_CALL_FLAGS_NONE,
609 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
610 g_clear_error (&error);
614 g_variant_get (result, "(v)", &username);
615 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
619 g_variant_get (username, "&s", &name);
621 g_debug ("Loaded session %s (%s)", path, name);
622 session = g_object_new (session_get_type (), NULL);
623 session->username = g_strdup (name);
624 session->path = g_strdup (path);
625 priv->sessions = g_list_append (priv->sessions, session);
627 g_variant_unref (username);
628 g_variant_unref (result);
634 display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
636 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
638 if (strcmp (signal_name, "SessionAdded") == 0)
640 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
644 LightDMUser *user = NULL;
646 g_variant_get (parameters, "(&o)", &path);
647 session = load_session (user_list, path);
649 user = get_user_by_name (user_list, session->username);
651 g_signal_emit (user, user_signals[CHANGED], 0);
654 else if (strcmp (signal_name, "SessionRemoved") == 0)
656 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
661 g_variant_get (parameters, "(&o)", &path);
663 for (link = priv->sessions; link; link = link->next)
665 Session *session = link->data;
666 if (strcmp (session->path, path) == 0)
670 g_debug ("Session %s removed", path);
671 priv->sessions = g_list_remove_link (priv->sessions, link);
672 user = get_user_by_name (user_list, session->username);
674 g_signal_emit (user, user_signals[CHANGED], 0);
675 g_object_unref (session);
684 update_users (LightDMUserList *user_list)
686 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
687 GError *error = NULL;
689 if (priv->have_users)
691 priv->have_users = TRUE;
693 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
694 G_DBUS_PROXY_FLAGS_NONE,
696 "org.freedesktop.Accounts",
697 "/org/freedesktop/Accounts",
698 "org.freedesktop.Accounts",
702 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
703 g_clear_error (&error);
705 /* Check if the service exists */
706 if (priv->accounts_service_proxy)
710 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
713 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
714 g_object_unref (priv->accounts_service_proxy);
715 priv->accounts_service_proxy = NULL;
720 if (priv->accounts_service_proxy)
724 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
726 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
728 g_variant_new ("()"),
729 G_DBUS_CALL_FLAGS_NONE,
734 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
735 g_clear_error (&error);
739 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
744 g_debug ("Loading users from org.freedesktop.Accounts");
745 g_variant_get (result, "(ao)", &iter);
746 while (g_variant_iter_loop (iter, "&o", &path))
748 UserAccountObject *object;
750 g_debug ("Loading user %s", path);
752 object = user_account_object_new (user_list, path);
753 if (object && update_user (object))
755 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
756 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
757 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
760 user_account_object_free (object);
762 g_variant_iter_free (iter);
765 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
767 g_variant_unref (result);
773 load_passwd_file (user_list, FALSE);
775 /* Watch for changes to user list */
776 passwd_file = g_file_new_for_path (PASSWD_FILE);
777 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
778 g_object_unref (passwd_file);
780 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
782 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
783 g_clear_error (&error);
786 priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
787 G_DBUS_PROXY_FLAGS_NONE,
789 "org.freedesktop.DisplayManager",
790 "/org/freedesktop/DisplayManager",
791 "org.freedesktop.DisplayManager",
795 g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
796 g_clear_error (&error);
798 if (priv->display_manager_proxy)
802 g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
804 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
805 "org.freedesktop.DisplayManager",
806 "/org/freedesktop/DisplayManager",
807 "org.freedesktop.DBus.Properties",
809 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
810 G_VARIANT_TYPE ("(v)"),
811 G_DBUS_CALL_FLAGS_NONE,
816 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
817 g_clear_error (&error);
821 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
827 g_variant_get (result, "(v)", &value);
829 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
830 g_variant_get (value, "ao", &iter);
831 while (g_variant_iter_loop (iter, "&o", &path))
832 load_session (user_list, path);
833 g_variant_iter_free (iter);
835 g_variant_unref (value);
838 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
840 g_variant_unref (result);
845 * lightdm_user_list_get_length:
846 * @user_list: a #LightDMUserList
848 * Return value: The number of users able to log in
851 lightdm_user_list_get_length (LightDMUserList *user_list)
853 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
854 update_users (user_list);
855 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
859 * lightdm_user_list_get_users:
860 * @user_list: A #LightDMUserList
862 * Get a list of users to present to the user. This list may be a subset of the
863 * available users and may be empty depending on the server configuration.
865 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
868 lightdm_user_list_get_users (LightDMUserList *user_list)
870 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
871 update_users (user_list);
872 return GET_LIST_PRIVATE (user_list)->users;
876 * lightdm_user_list_get_user_by_name:
877 * @user_list: A #LightDMUserList
878 * @username: Name of user to get.
880 * Get infomation about a given user or #NULL if this user doesn't exist.
882 * Return value: (transfer none): A #LightDMUser entry for the given user.
885 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
887 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
888 g_return_val_if_fail (username != NULL, NULL);
890 update_users (user_list);
892 return get_user_by_name (user_list, username);
896 lightdm_user_list_init (LightDMUserList *user_list)
901 lightdm_user_list_set_property (GObject *object,
906 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
910 lightdm_user_list_get_property (GObject *object,
915 LightDMUserList *self;
917 self = LIGHTDM_USER_LIST (object);
921 case LIST_PROP_NUM_USERS:
922 g_value_set_int (value, lightdm_user_list_get_length (self));
925 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
931 lightdm_user_list_finalize (GObject *object)
933 LightDMUserList *self = LIGHTDM_USER_LIST (object);
934 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
936 if (priv->accounts_service_proxy)
937 g_object_unref (priv->accounts_service_proxy);
938 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
939 if (priv->passwd_monitor)
940 g_object_unref (priv->passwd_monitor);
941 g_list_free_full (priv->users, g_object_unref);
942 g_list_free_full (priv->sessions, g_object_unref);
944 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
948 lightdm_user_list_class_init (LightDMUserListClass *klass)
950 GObjectClass *object_class = G_OBJECT_CLASS (klass);
952 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
954 object_class->set_property = lightdm_user_list_set_property;
955 object_class->get_property = lightdm_user_list_get_property;
956 object_class->finalize = lightdm_user_list_finalize;
958 g_object_class_install_property (object_class,
960 g_param_spec_int ("num-users",
962 "Number of login users",
966 * LightDMUserList::user-added:
967 * @user_list: A #LightDMUserList
968 * @user: The #LightDM user that has been added.
970 * The ::user-added signal gets emitted when a user account is created.
972 list_signals[USER_ADDED] =
973 g_signal_new ("user-added",
974 G_TYPE_FROM_CLASS (klass),
976 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
978 g_cclosure_marshal_VOID__OBJECT,
979 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
982 * LightDMUserList::user-changed:
983 * @user_list: A #LightDMUserList
984 * @user: The #LightDM user that has been changed.
986 * The ::user-changed signal gets emitted when a user account is modified.
988 list_signals[USER_CHANGED] =
989 g_signal_new ("user-changed",
990 G_TYPE_FROM_CLASS (klass),
992 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
994 g_cclosure_marshal_VOID__OBJECT,
995 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
998 * LightDMUserList::user-removed:
999 * @user_list: A #LightDMUserList
1000 * @user: The #LightDM user that has been removed.
1002 * The ::user-removed signal gets emitted when a user account is removed.
1004 list_signals[USER_REMOVED] =
1005 g_signal_new ("user-removed",
1006 G_TYPE_FROM_CLASS (klass),
1008 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1010 g_cclosure_marshal_VOID__OBJECT,
1011 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1015 * lightdm_user_get_name:
1016 * @user: A #LightDMUser
1018 * Get the name of a user.
1020 * Return value: The name of the given user
1023 lightdm_user_get_name (LightDMUser *user)
1025 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1026 return GET_USER_PRIVATE (user)->name;
1030 * lightdm_user_get_real_name:
1031 * @user: A #LightDMUser
1033 * Get the real name of a user.
1035 * Return value: The real name of the given user
1038 lightdm_user_get_real_name (LightDMUser *user)
1040 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1041 return GET_USER_PRIVATE (user)->real_name;
1045 * lightdm_user_get_display_name:
1046 * @user: A #LightDMUser
1048 * Get the display name of a user.
1050 * Return value: The display name of the given user
1053 lightdm_user_get_display_name (LightDMUser *user)
1055 LightDMUserPrivate *priv;
1057 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1059 priv = GET_USER_PRIVATE (user);
1060 if (strcmp (priv->real_name, ""))
1061 return priv->real_name;
1067 * lightdm_user_get_home_directory:
1068 * @user: A #LightDMUser
1070 * Get the home directory for a user.
1072 * Return value: The users home directory
1075 lightdm_user_get_home_directory (LightDMUser *user)
1077 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1078 return GET_USER_PRIVATE (user)->home_directory;
1082 * lightdm_user_get_image:
1083 * @user: A #LightDMUser
1085 * Get the image URI for a user.
1087 * Return value: The image URI for the given user or #NULL if no URI
1090 lightdm_user_get_image (LightDMUser *user)
1092 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1093 return GET_USER_PRIVATE (user)->image;
1097 * lightdm_user_get_background:
1098 * @user: A #LightDMUser
1100 * Get the background file path for a user.
1102 * Return value: The background file path for the given user or #NULL if no path
1105 lightdm_user_get_background (LightDMUser *user)
1107 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1108 return GET_USER_PRIVATE (user)->background;
1112 load_dmrc (LightDMUser *user)
1114 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1116 //gboolean have_dmrc;
1118 priv->dmrc_file = g_key_file_new ();
1120 /* Load from the user directory */
1121 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1122 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1125 /* If no ~/.dmrc, then load from the cache */
1128 // FIXME: Watch for changes
1131 g_free (priv->language);
1133 g_free (priv->layout);
1135 g_free (priv->session);
1137 /* The Language field is actually a locale, strip the codeset off it to get the language */
1138 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1141 gchar *codeset = strchr (priv->language, '.');
1146 priv->layout = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1147 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1151 get_string_property (GDBusProxy *proxy, const gchar *property)
1159 answer = g_dbus_proxy_get_cached_property (proxy, property);
1163 g_warning ("Could not get accounts property %s", property);
1167 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("s")))
1169 g_warning ("Unexpected accounts property type for %s: %s",
1170 property, g_variant_get_type_string (answer));
1171 g_variant_unref (answer);
1175 g_variant_get (answer, "s", &rv);
1177 g_variant_unref (answer);
1182 load_accounts_service (LightDMUser *user)
1184 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1185 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1187 /* First, find AccountObject proxy */
1188 UserAccountObject *account = NULL;
1190 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1192 if (((UserAccountObject *)iter->data)->user == user)
1194 account = (UserAccountObject *)iter->data;
1201 /* We have proxy, let's grab some properties */
1203 g_free (priv->language);
1205 g_free (priv->session);
1206 priv->language = get_string_property (account->proxy, "Language");
1207 priv->session = get_string_property (account->proxy, "XSession");
1212 /* Loads language/layout/session info for user */
1214 load_user_values (LightDMUser *user)
1217 load_accounts_service (user); // overrides dmrc values
1221 * lightdm_user_get_language
1222 * @user: A #LightDMUser
1224 * Get the language for a user.
1226 * Return value: The language for the given user or #NULL if using system defaults.
1229 lightdm_user_get_language (LightDMUser *user)
1231 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1232 load_user_values (user);
1233 return GET_USER_PRIVATE (user)->language;
1237 * lightdm_user_get_layout
1238 * @user: A #LightDMUser
1240 * Get the keyboard layout for a user.
1242 * Return value: The keyboard layoyt for the given user or #NULL if using system defaults.
1245 lightdm_user_get_layout (LightDMUser *user)
1247 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1248 load_user_values (user);
1249 return GET_USER_PRIVATE (user)->layout;
1253 * lightdm_user_get_session
1254 * @user: A #LightDMUser
1256 * Get the session for a user.
1258 * Return value: The session for the given user or #NULL if using system defaults.
1261 lightdm_user_get_session (LightDMUser *user)
1263 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1264 load_user_values (user);
1265 return GET_USER_PRIVATE (user)->session;
1269 * lightdm_user_get_logged_in:
1270 * @user: A #LightDMUser
1272 * Check if a user is logged in.
1274 * Return value: #TRUE if the user is currently logged in.
1277 lightdm_user_get_logged_in (LightDMUser *user)
1279 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1280 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1283 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1285 for (link = list_priv->sessions; link; link = link->next)
1287 Session *session = link->data;
1288 if (strcmp (session->username, priv->name) == 0)
1296 lightdm_user_init (LightDMUser *user)
1298 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1300 priv->name = g_strdup ("");
1301 priv->real_name = g_strdup ("");
1302 priv->home_directory = g_strdup ("");
1303 priv->image = g_strdup ("");
1304 priv->background = g_strdup ("");
1305 priv->language = g_strdup ("");
1306 priv->layout = g_strdup ("");
1307 priv->session = g_strdup ("");
1311 lightdm_user_set_property (GObject *object,
1313 const GValue *value,
1316 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1320 lightdm_user_get_property (GObject *object,
1327 self = LIGHTDM_USER (object);
1331 case USER_PROP_NAME:
1332 g_value_set_string (value, lightdm_user_get_name (self));
1334 case USER_PROP_REAL_NAME:
1335 g_value_set_string (value, lightdm_user_get_real_name (self));
1337 case USER_PROP_DISPLAY_NAME:
1338 g_value_set_string (value, lightdm_user_get_display_name (self));
1340 case USER_PROP_HOME_DIRECTORY:
1341 g_value_set_string (value, lightdm_user_get_home_directory (self));
1343 case USER_PROP_IMAGE:
1344 g_value_set_string (value, lightdm_user_get_image (self));
1346 case USER_PROP_BACKGROUND:
1347 g_value_set_string (value, lightdm_user_get_background (self));
1349 case USER_PROP_LANGUAGE:
1350 g_value_set_string (value, lightdm_user_get_language (self));
1352 case USER_PROP_LAYOUT:
1353 g_value_set_string (value, lightdm_user_get_layout (self));
1355 case USER_PROP_SESSION:
1356 g_value_set_string (value, lightdm_user_get_session (self));
1358 case USER_PROP_LOGGED_IN:
1359 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1368 lightdm_user_finalize (GObject *object)
1370 LightDMUser *self = LIGHTDM_USER (object);
1371 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1373 g_free (priv->name);
1374 g_free (priv->real_name);
1375 g_free (priv->home_directory);
1376 g_free (priv->image);
1377 g_free (priv->background);
1378 if (priv->dmrc_file)
1379 g_key_file_free (priv->dmrc_file);
1383 lightdm_user_class_init (LightDMUserClass *klass)
1385 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1387 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1389 object_class->set_property = lightdm_user_set_property;
1390 object_class->get_property = lightdm_user_get_property;
1391 object_class->finalize = lightdm_user_finalize;
1393 g_object_class_install_property (object_class,
1395 g_param_spec_string ("name",
1399 G_PARAM_READWRITE));
1400 g_object_class_install_property (object_class,
1401 USER_PROP_REAL_NAME,
1402 g_param_spec_string ("real-name",
1406 G_PARAM_READWRITE));
1407 g_object_class_install_property (object_class,
1408 USER_PROP_DISPLAY_NAME,
1409 g_param_spec_string ("display-name",
1411 "Users display name",
1414 g_object_class_install_property (object_class,
1415 USER_PROP_HOME_DIRECTORY,
1416 g_param_spec_string ("home-directory",
1420 G_PARAM_READWRITE));
1421 g_object_class_install_property (object_class,
1423 g_param_spec_string ("image",
1427 G_PARAM_READWRITE));
1428 g_object_class_install_property (object_class,
1429 USER_PROP_BACKGROUND,
1430 g_param_spec_string ("background",
1434 G_PARAM_READWRITE));
1435 g_object_class_install_property (object_class,
1437 g_param_spec_string ("language",
1439 "Language used by this user",
1442 g_object_class_install_property (object_class,
1444 g_param_spec_string ("layout",
1446 "Keyboard layout used by this user",
1449 g_object_class_install_property (object_class,
1451 g_param_spec_string ("session",
1453 "Session used by this user",
1456 g_object_class_install_property (object_class,
1457 USER_PROP_LOGGED_IN,
1458 g_param_spec_boolean ("logged-in",
1460 "TRUE if the user is currently in a session",
1462 G_PARAM_READWRITE));
1465 * LightDMUser::changed:
1466 * @user: A #LightDMUser
1468 * The ::changed signal gets emitted this user account is modified.
1470 user_signals[CHANGED] =
1471 g_signal_new ("changed",
1472 G_TYPE_FROM_CLASS (klass),
1474 G_STRUCT_OFFSET (LightDMUserClass, changed),
1476 g_cclosure_marshal_VOID__VOID,
1481 session_init (Session *session)
1486 session_finalize (GObject *object)
1488 Session *self = SESSION (object);
1490 g_free (self->path);
1491 g_free (self->username);
1495 session_class_init (SessionClass *klass)
1497 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1498 object_class->finalize = session_finalize;