1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
3 * Copyright (C) 2010 Robert Ancell.
4 * Author: Robert Ancell <robert.ancell@canonical.com>
6 * This library is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by the Free
8 * Software Foundation; either version 3 of the License, or (at your option) any
9 * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
17 #include <sys/utsname.h>
21 #include "lightdm/user.h"
35 USER_PROP_DISPLAY_NAME,
36 USER_PROP_HOME_DIRECTORY,
52 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
59 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
63 /* Connection to AccountsService */
64 GDBusProxy *accounts_service_proxy;
65 GList *user_account_objects;
67 /* Connection to DisplayManager */
68 GDBusProxy *display_manager_proxy;
70 /* File monitor for password file */
71 GFileMonitor *passwd_monitor;
73 /* TRUE if have scanned users */
79 /* List of sessions */
81 } LightDMUserListPrivate;
91 LightDMUserList *user_list;
95 gchar *home_directory;
103 } LightDMUserPrivate;
107 GObject parent_instance;
114 GObjectClass parent_class;
117 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
118 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
119 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
120 GType session_get_type (void);
121 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
123 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
124 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
126 #define PASSWD_FILE "/etc/passwd"
127 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
129 static LightDMUserList *singleton = NULL;
132 * lightdm_user_list_get_instance:
136 * Return value: (transfer none): the #LightDMUserList
139 lightdm_user_list_get_instance (void)
142 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
147 get_user_by_name (LightDMUserList *user_list, const gchar *username)
149 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
152 for (link = priv->users; link; link = link->next)
154 LightDMUser *user = link->data;
155 if (strcmp (lightdm_user_get_name (user), username) == 0)
163 compare_user (gconstpointer a, gconstpointer b)
165 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
166 return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
170 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
172 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
174 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
175 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
176 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
179 g_free (priv->real_name);
180 priv->real_name = g_strdup (real_name);
181 g_free (priv->home_directory);
182 priv->home_directory = g_strdup (home_directory);
183 g_free (priv->image);
184 priv->image = g_strdup (image);
190 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
192 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
196 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
198 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
202 gchar **hidden_users, **hidden_shells;
203 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
204 GError *error = NULL;
206 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
208 config = g_key_file_new ();
209 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
210 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
211 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
212 g_clear_error (&error);
214 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
215 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
219 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
221 value = g_strdup ("nobody nobody4 noaccess");
222 hidden_users = g_strsplit (value, " ", -1);
225 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
227 value = g_strdup ("/bin/false /usr/sbin/nologin");
228 hidden_shells = g_strsplit (value, " ", -1);
231 g_key_file_free (config);
237 struct passwd *entry;
239 LightDMUserPrivate *user_priv;
241 gchar *real_name, *image;
249 /* Ignore system users */
250 if (entry->pw_uid < minimum_uid)
253 /* Ignore users disabled by shell */
256 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
257 if (hidden_shells[i])
261 /* Ignore certain users */
262 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
266 tokens = g_strsplit (entry->pw_gecos, ",", -1);
267 if (tokens[0] != NULL && tokens[0][0] != '\0')
268 real_name = g_strdup (tokens[0]);
270 real_name = g_strdup ("");
273 image = g_build_filename (entry->pw_dir, ".face", NULL);
274 if (!g_file_test (image, G_FILE_TEST_EXISTS))
277 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
278 if (!g_file_test (image, G_FILE_TEST_EXISTS))
285 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
286 user_priv = GET_USER_PRIVATE (user);
287 user_priv->user_list = user_list;
288 g_free (user_priv->name);
289 user_priv->name = g_strdup (entry->pw_name);
290 g_free (user_priv->real_name);
291 user_priv->real_name = real_name;
292 g_free (user_priv->home_directory);
293 user_priv->home_directory = g_strdup (entry->pw_dir);
294 g_free (user_priv->image);
295 user_priv->image = image;
297 /* Update existing users if have them */
298 for (link = priv->users; link; link = link->next)
300 LightDMUser *info = link->data;
301 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
303 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
304 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
305 g_object_unref (user);
312 /* Only notify once we have loaded the user list */
313 if (priv->have_users)
314 new_users = g_list_insert_sorted (new_users, user, compare_user);
316 users = g_list_insert_sorted (users, user, compare_user);
318 g_strfreev (hidden_users);
319 g_strfreev (hidden_shells);
322 g_warning ("Failed to read password database: %s", strerror (errno));
326 /* Use new user list */
327 old_users = priv->users;
330 /* Notify of changes */
331 for (link = new_users; link; link = link->next)
333 LightDMUser *info = link->data;
334 g_debug ("User %s added", lightdm_user_get_name (info));
335 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
337 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
339 g_list_free (new_users);
340 for (link = changed_users; link; link = link->next)
342 LightDMUser *info = link->data;
343 g_debug ("User %s changed", lightdm_user_get_name (info));
344 g_signal_emit (info, user_signals[CHANGED], 0);
346 g_list_free (changed_users);
347 for (link = old_users; link; link = link->next)
351 /* See if this user is in the current list */
352 for (new_link = priv->users; new_link; new_link = new_link->next)
354 if (new_link->data == link->data)
360 LightDMUser *info = link->data;
361 g_debug ("User %s removed", lightdm_user_get_name (info));
362 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
363 g_object_unref (info);
366 g_list_free (old_users);
370 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
372 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
374 g_debug ("%s changed, reloading user list", g_file_get_path (file));
375 load_passwd_file (user_list, TRUE);
380 update_user (UserAccountObject *object)
382 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
383 GVariant *result, *value;
386 GError *error = NULL;
388 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
389 "org.freedesktop.Accounts",
390 g_dbus_proxy_get_object_path (object->proxy),
391 "org.freedesktop.DBus.Properties",
393 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
394 G_VARIANT_TYPE ("(a{sv})"),
395 G_DBUS_CALL_FLAGS_NONE,
400 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
401 g_clear_error (&error);
405 g_variant_get (result, "(a{sv})", &iter);
406 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
408 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
411 g_variant_get (value, "&s", &user_name);
413 priv->name = g_strdup (user_name);
415 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
418 g_variant_get (value, "&s", &real_name);
419 g_free (priv->real_name);
420 priv->real_name = g_strdup (real_name);
422 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
424 gchar *home_directory;
425 g_variant_get (value, "&s", &home_directory);
426 g_free (priv->home_directory);
427 priv->home_directory = g_strdup (home_directory);
429 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
432 g_variant_get (value, "&s", &icon_file);
433 g_free (priv->image);
434 if (strcmp (icon_file, "") == 0)
437 priv->image = g_strdup (icon_file);
439 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
441 gchar *background_file;
442 g_variant_get (value, "&s", &background_file);
443 g_free (priv->background);
444 if (strcmp (background_file, "") == 0)
445 priv->background = NULL;
447 priv->background = g_strdup (background_file);
450 g_variant_iter_free (iter);
452 g_variant_unref (result);
458 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
460 if (strcmp (signal_name, "Changed") == 0)
462 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
464 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
465 update_user (object);
466 g_signal_emit (object->user, user_signals[CHANGED], 0);
469 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
473 static UserAccountObject *
474 user_account_object_new (LightDMUserList *user_list, const gchar *path)
477 UserAccountObject *object;
478 GError *error = NULL;
480 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
481 G_DBUS_PROXY_FLAGS_NONE,
483 "org.freedesktop.Accounts",
485 "org.freedesktop.Accounts.User",
489 g_warning ("Error getting user %s: %s", path, error->message);
490 g_clear_error (&error);
494 object = g_malloc0 (sizeof (UserAccountObject));
495 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
496 GET_USER_PRIVATE (object->user)->user_list = user_list;
497 object->proxy = proxy;
498 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
504 user_account_object_free (UserAccountObject *object)
508 g_object_unref (object->user);
509 g_object_unref (object->proxy);
513 static UserAccountObject *
514 find_user_account_object (LightDMUserList *user_list, const gchar *path)
516 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
519 for (link = priv->user_account_objects; link; link = link->next)
521 UserAccountObject *object = link->data;
522 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
530 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
532 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
534 if (strcmp (signal_name, "UserAdded") == 0)
536 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
539 UserAccountObject *object;
541 g_variant_get (parameters, "(&o)", &path);
543 /* Ignore duplicate requests */
544 object = find_user_account_object (user_list, path);
548 object = user_account_object_new (user_list, path);
549 if (object && update_user (object))
551 g_debug ("User %s added", path);
552 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
553 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
554 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
555 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
558 user_account_object_free (object);
561 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
563 else if (strcmp (signal_name, "UserDeleted") == 0)
565 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
568 UserAccountObject *object;
570 g_variant_get (parameters, "(&o)", &path);
572 object = find_user_account_object (user_list, path);
576 g_debug ("User %s deleted", path);
577 priv->users = g_list_remove (priv->users, object->user);
578 g_object_unref (object->user);
580 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
582 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
583 user_account_object_free (object);
586 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
591 load_session (LightDMUserList *user_list, const gchar *path)
593 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
594 Session *session = NULL;
595 GVariant *result, *username;
596 GError *error = NULL;
598 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
599 "org.freedesktop.DisplayManager",
601 "org.freedesktop.DBus.Properties",
603 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
604 G_VARIANT_TYPE ("(v)"),
605 G_DBUS_CALL_FLAGS_NONE,
610 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
611 g_clear_error (&error);
615 g_variant_get (result, "(v)", &username);
616 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
620 g_variant_get (username, "&s", &name);
622 g_debug ("Loaded session %s (%s)", path, name);
623 session = g_object_new (session_get_type (), NULL);
624 session->username = g_strdup (name);
625 session->path = g_strdup (path);
626 priv->sessions = g_list_append (priv->sessions, session);
628 g_variant_unref (username);
629 g_variant_unref (result);
635 display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
637 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
639 if (strcmp (signal_name, "SessionAdded") == 0)
641 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
645 LightDMUser *user = NULL;
647 g_variant_get (parameters, "(&o)", &path);
648 session = load_session (user_list, path);
650 user = get_user_by_name (user_list, session->username);
652 g_signal_emit (user, user_signals[CHANGED], 0);
655 else if (strcmp (signal_name, "SessionRemoved") == 0)
657 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
662 g_variant_get (parameters, "(&o)", &path);
664 for (link = priv->sessions; link; link = link->next)
666 Session *session = link->data;
667 if (strcmp (session->path, path) == 0)
671 g_debug ("Session %s removed", path);
672 priv->sessions = g_list_remove_link (priv->sessions, link);
673 user = get_user_by_name (user_list, session->username);
675 g_signal_emit (user, user_signals[CHANGED], 0);
676 g_object_unref (session);
685 update_users (LightDMUserList *user_list)
687 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
688 GError *error = NULL;
690 if (priv->have_users)
692 priv->have_users = TRUE;
694 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
695 G_DBUS_PROXY_FLAGS_NONE,
697 "org.freedesktop.Accounts",
698 "/org/freedesktop/Accounts",
699 "org.freedesktop.Accounts",
703 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
704 g_clear_error (&error);
706 /* Check if the service exists */
707 if (priv->accounts_service_proxy)
711 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
714 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
715 g_object_unref (priv->accounts_service_proxy);
716 priv->accounts_service_proxy = NULL;
721 if (priv->accounts_service_proxy)
725 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
727 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
729 g_variant_new ("()"),
730 G_DBUS_CALL_FLAGS_NONE,
735 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
736 g_clear_error (&error);
740 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
745 g_debug ("Loading users from org.freedesktop.Accounts");
746 g_variant_get (result, "(ao)", &iter);
747 while (g_variant_iter_loop (iter, "&o", &path))
749 UserAccountObject *object;
751 g_debug ("Loading user %s", path);
753 object = user_account_object_new (user_list, path);
754 if (object && update_user (object))
756 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
757 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
758 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
761 user_account_object_free (object);
763 g_variant_iter_free (iter);
766 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
768 g_variant_unref (result);
772 const gchar *passwd_filename;
775 load_passwd_file (user_list, FALSE);
777 /* Watch for changes to user list */
779 passwd_filename = g_getenv ("LIGHTDM_TEST_PASSWD_FILE");
780 if (!passwd_filename)
781 passwd_filename = PASSWD_FILE;
782 passwd_file = g_file_new_for_path (PASSWD_FILE);
783 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
784 g_object_unref (passwd_file);
786 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
788 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
789 g_clear_error (&error);
792 priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
793 G_DBUS_PROXY_FLAGS_NONE,
795 "org.freedesktop.DisplayManager",
796 "/org/freedesktop/DisplayManager",
797 "org.freedesktop.DisplayManager",
801 g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
802 g_clear_error (&error);
804 if (priv->display_manager_proxy)
808 g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
810 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
811 "org.freedesktop.DisplayManager",
812 "/org/freedesktop/DisplayManager",
813 "org.freedesktop.DBus.Properties",
815 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
816 G_VARIANT_TYPE ("(v)"),
817 G_DBUS_CALL_FLAGS_NONE,
822 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
823 g_clear_error (&error);
827 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
833 g_variant_get (result, "(v)", &value);
835 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
836 g_variant_get (value, "ao", &iter);
837 while (g_variant_iter_loop (iter, "&o", &path))
838 load_session (user_list, path);
839 g_variant_iter_free (iter);
841 g_variant_unref (value);
844 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
846 g_variant_unref (result);
851 * lightdm_user_list_get_length:
852 * @user_list: a #LightDMUserList
854 * Return value: The number of users able to log in
857 lightdm_user_list_get_length (LightDMUserList *user_list)
859 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
860 update_users (user_list);
861 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
865 * lightdm_user_list_get_users:
866 * @user_list: A #LightDMUserList
868 * Get a list of users to present to the user. This list may be a subset of the
869 * available users and may be empty depending on the server configuration.
871 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
874 lightdm_user_list_get_users (LightDMUserList *user_list)
876 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
877 update_users (user_list);
878 return GET_LIST_PRIVATE (user_list)->users;
882 * lightdm_user_list_get_user_by_name:
883 * @user_list: A #LightDMUserList
884 * @username: Name of user to get.
886 * Get infomation about a given user or #NULL if this user doesn't exist.
888 * Return value: (transfer none): A #LightDMUser entry for the given user.
891 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
893 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
894 g_return_val_if_fail (username != NULL, NULL);
896 update_users (user_list);
898 return get_user_by_name (user_list, username);
902 lightdm_user_list_init (LightDMUserList *user_list)
907 lightdm_user_list_set_property (GObject *object,
912 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
916 lightdm_user_list_get_property (GObject *object,
921 LightDMUserList *self;
923 self = LIGHTDM_USER_LIST (object);
927 case LIST_PROP_NUM_USERS:
928 g_value_set_int (value, lightdm_user_list_get_length (self));
931 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
937 lightdm_user_list_finalize (GObject *object)
939 LightDMUserList *self = LIGHTDM_USER_LIST (object);
940 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
942 if (priv->accounts_service_proxy)
943 g_object_unref (priv->accounts_service_proxy);
944 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
945 if (priv->passwd_monitor)
946 g_object_unref (priv->passwd_monitor);
947 g_list_free_full (priv->users, g_object_unref);
948 g_list_free_full (priv->sessions, g_object_unref);
950 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
954 lightdm_user_list_class_init (LightDMUserListClass *klass)
956 GObjectClass *object_class = G_OBJECT_CLASS (klass);
958 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
960 object_class->set_property = lightdm_user_list_set_property;
961 object_class->get_property = lightdm_user_list_get_property;
962 object_class->finalize = lightdm_user_list_finalize;
964 g_object_class_install_property (object_class,
966 g_param_spec_int ("num-users",
968 "Number of login users",
972 * LightDMUserList::user-added:
973 * @user_list: A #LightDMUserList
974 * @user: The #LightDM user that has been added.
976 * The ::user-added signal gets emitted when a user account is created.
978 list_signals[USER_ADDED] =
979 g_signal_new ("user-added",
980 G_TYPE_FROM_CLASS (klass),
982 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
984 g_cclosure_marshal_VOID__OBJECT,
985 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
988 * LightDMUserList::user-changed:
989 * @user_list: A #LightDMUserList
990 * @user: The #LightDM user that has been changed.
992 * The ::user-changed signal gets emitted when a user account is modified.
994 list_signals[USER_CHANGED] =
995 g_signal_new ("user-changed",
996 G_TYPE_FROM_CLASS (klass),
998 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
1000 g_cclosure_marshal_VOID__OBJECT,
1001 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1004 * LightDMUserList::user-removed:
1005 * @user_list: A #LightDMUserList
1006 * @user: The #LightDM user that has been removed.
1008 * The ::user-removed signal gets emitted when a user account is removed.
1010 list_signals[USER_REMOVED] =
1011 g_signal_new ("user-removed",
1012 G_TYPE_FROM_CLASS (klass),
1014 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1016 g_cclosure_marshal_VOID__OBJECT,
1017 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1021 * lightdm_user_get_name:
1022 * @user: A #LightDMUser
1024 * Get the name of a user.
1026 * Return value: The name of the given user
1029 lightdm_user_get_name (LightDMUser *user)
1031 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1032 return GET_USER_PRIVATE (user)->name;
1036 * lightdm_user_get_real_name:
1037 * @user: A #LightDMUser
1039 * Get the real name of a user.
1041 * Return value: The real name of the given user
1044 lightdm_user_get_real_name (LightDMUser *user)
1046 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1047 return GET_USER_PRIVATE (user)->real_name;
1051 * lightdm_user_get_display_name:
1052 * @user: A #LightDMUser
1054 * Get the display name of a user.
1056 * Return value: The display name of the given user
1059 lightdm_user_get_display_name (LightDMUser *user)
1061 LightDMUserPrivate *priv;
1063 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1065 priv = GET_USER_PRIVATE (user);
1066 if (strcmp (priv->real_name, ""))
1067 return priv->real_name;
1073 * lightdm_user_get_home_directory:
1074 * @user: A #LightDMUser
1076 * Get the home directory for a user.
1078 * Return value: The users home directory
1081 lightdm_user_get_home_directory (LightDMUser *user)
1083 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1084 return GET_USER_PRIVATE (user)->home_directory;
1088 * lightdm_user_get_image:
1089 * @user: A #LightDMUser
1091 * Get the image URI for a user.
1093 * Return value: The image URI for the given user or #NULL if no URI
1096 lightdm_user_get_image (LightDMUser *user)
1098 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1099 return GET_USER_PRIVATE (user)->image;
1103 * lightdm_user_get_background:
1104 * @user: A #LightDMUser
1106 * Get the background file path for a user.
1108 * Return value: The background file path for the given user or #NULL if no path
1111 lightdm_user_get_background (LightDMUser *user)
1113 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1114 return GET_USER_PRIVATE (user)->background;
1118 load_dmrc (LightDMUser *user)
1120 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1122 //gboolean have_dmrc;
1124 priv->dmrc_file = g_key_file_new ();
1126 /* Load from the user directory */
1127 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1128 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1131 /* If no ~/.dmrc, then load from the cache */
1134 // FIXME: Watch for changes
1136 /* The Language field is actually a locale, strip the codeset off it to get the language */
1138 g_free (priv->language);
1139 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1142 gchar *codeset = strchr (priv->language, '.');
1148 g_free (priv->layout);
1149 priv->layout = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1152 g_free (priv->session);
1153 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1157 get_string_property (GDBusProxy *proxy, const gchar *property)
1165 answer = g_dbus_proxy_get_cached_property (proxy, property);
1169 g_warning ("Could not get accounts property %s", property);
1173 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("s")))
1175 g_warning ("Unexpected accounts property type for %s: %s",
1176 property, g_variant_get_type_string (answer));
1177 g_variant_unref (answer);
1181 g_variant_get (answer, "s", &rv);
1183 if (strcmp (rv, "") == 0)
1189 g_variant_unref (answer);
1194 load_accounts_service (LightDMUser *user)
1196 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1197 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1198 UserAccountObject *account = NULL;
1202 /* First, find AccountObject proxy */
1203 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1205 UserAccountObject *a = iter->data;
1206 if (a->user == user)
1215 /* We have proxy, let's grab some properties */
1217 g_free (priv->language);
1218 priv->language = get_string_property (account->proxy, "Language");
1220 g_free (priv->session);
1221 priv->session = get_string_property (account->proxy, "XSession");
1223 value = get_string_property (account->proxy, "XKeyboardLayout");
1226 g_free (priv->layout);
1227 priv->layout = value;
1233 /* Loads language/layout/session info for user */
1235 load_user_values (LightDMUser *user)
1238 load_accounts_service (user); // overrides dmrc values
1242 * lightdm_user_get_language
1243 * @user: A #LightDMUser
1245 * Get the language for a user.
1247 * Return value: The language for the given user or #NULL if using system defaults.
1250 lightdm_user_get_language (LightDMUser *user)
1252 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1253 load_user_values (user);
1254 return GET_USER_PRIVATE (user)->language;
1258 * lightdm_user_get_layout
1259 * @user: A #LightDMUser
1261 * Get the keyboard layout for a user.
1263 * Return value: The keyboard layoyt for the given user or #NULL if using system defaults.
1266 lightdm_user_get_layout (LightDMUser *user)
1268 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1269 load_user_values (user);
1270 return GET_USER_PRIVATE (user)->layout;
1274 * lightdm_user_get_session
1275 * @user: A #LightDMUser
1277 * Get the session for a user.
1279 * Return value: The session for the given user or #NULL if using system defaults.
1282 lightdm_user_get_session (LightDMUser *user)
1284 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1285 load_user_values (user);
1286 return GET_USER_PRIVATE (user)->session;
1290 * lightdm_user_get_logged_in:
1291 * @user: A #LightDMUser
1293 * Check if a user is logged in.
1295 * Return value: #TRUE if the user is currently logged in.
1298 lightdm_user_get_logged_in (LightDMUser *user)
1300 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1301 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1304 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1306 for (link = list_priv->sessions; link; link = link->next)
1308 Session *session = link->data;
1309 if (strcmp (session->username, priv->name) == 0)
1317 lightdm_user_init (LightDMUser *user)
1322 lightdm_user_set_property (GObject *object,
1324 const GValue *value,
1327 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1331 lightdm_user_get_property (GObject *object,
1338 self = LIGHTDM_USER (object);
1342 case USER_PROP_NAME:
1343 g_value_set_string (value, lightdm_user_get_name (self));
1345 case USER_PROP_REAL_NAME:
1346 g_value_set_string (value, lightdm_user_get_real_name (self));
1348 case USER_PROP_DISPLAY_NAME:
1349 g_value_set_string (value, lightdm_user_get_display_name (self));
1351 case USER_PROP_HOME_DIRECTORY:
1352 g_value_set_string (value, lightdm_user_get_home_directory (self));
1354 case USER_PROP_IMAGE:
1355 g_value_set_string (value, lightdm_user_get_image (self));
1357 case USER_PROP_BACKGROUND:
1358 g_value_set_string (value, lightdm_user_get_background (self));
1360 case USER_PROP_LANGUAGE:
1361 g_value_set_string (value, lightdm_user_get_language (self));
1363 case USER_PROP_LAYOUT:
1364 g_value_set_string (value, lightdm_user_get_layout (self));
1366 case USER_PROP_SESSION:
1367 g_value_set_string (value, lightdm_user_get_session (self));
1369 case USER_PROP_LOGGED_IN:
1370 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1373 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1379 lightdm_user_finalize (GObject *object)
1381 LightDMUser *self = LIGHTDM_USER (object);
1382 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1384 g_free (priv->name);
1385 g_free (priv->real_name);
1386 g_free (priv->home_directory);
1387 g_free (priv->image);
1388 g_free (priv->background);
1389 if (priv->dmrc_file)
1390 g_key_file_free (priv->dmrc_file);
1394 lightdm_user_class_init (LightDMUserClass *klass)
1396 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1398 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1400 object_class->set_property = lightdm_user_set_property;
1401 object_class->get_property = lightdm_user_get_property;
1402 object_class->finalize = lightdm_user_finalize;
1404 g_object_class_install_property (object_class,
1406 g_param_spec_string ("name",
1410 G_PARAM_READWRITE));
1411 g_object_class_install_property (object_class,
1412 USER_PROP_REAL_NAME,
1413 g_param_spec_string ("real-name",
1417 G_PARAM_READWRITE));
1418 g_object_class_install_property (object_class,
1419 USER_PROP_DISPLAY_NAME,
1420 g_param_spec_string ("display-name",
1422 "Users display name",
1425 g_object_class_install_property (object_class,
1426 USER_PROP_HOME_DIRECTORY,
1427 g_param_spec_string ("home-directory",
1431 G_PARAM_READWRITE));
1432 g_object_class_install_property (object_class,
1434 g_param_spec_string ("image",
1438 G_PARAM_READWRITE));
1439 g_object_class_install_property (object_class,
1440 USER_PROP_BACKGROUND,
1441 g_param_spec_string ("background",
1445 G_PARAM_READWRITE));
1446 g_object_class_install_property (object_class,
1448 g_param_spec_string ("language",
1450 "Language used by this user",
1453 g_object_class_install_property (object_class,
1455 g_param_spec_string ("layout",
1457 "Keyboard layout used by this user",
1460 g_object_class_install_property (object_class,
1462 g_param_spec_string ("session",
1464 "Session used by this user",
1467 g_object_class_install_property (object_class,
1468 USER_PROP_LOGGED_IN,
1469 g_param_spec_boolean ("logged-in",
1471 "TRUE if the user is currently in a session",
1473 G_PARAM_READWRITE));
1476 * LightDMUser::changed:
1477 * @user: A #LightDMUser
1479 * The ::changed signal gets emitted this user account is modified.
1481 user_signals[CHANGED] =
1482 g_signal_new ("changed",
1483 G_TYPE_FROM_CLASS (klass),
1485 G_STRUCT_OFFSET (LightDMUserClass, changed),
1487 g_cclosure_marshal_VOID__VOID,
1492 session_init (Session *session)
1497 session_finalize (GObject *object)
1499 Session *self = SESSION (object);
1501 g_free (self->path);
1502 g_free (self->username);
1506 session_class_init (SessionClass *klass)
1508 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1509 object_class->finalize = session_finalize;