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 2 or version 3 of the License.
9 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
16 #include <sys/utsname.h>
20 #include "lightdm/user.h"
34 USER_PROP_DISPLAY_NAME,
35 USER_PROP_HOME_DIRECTORY,
43 USER_PROP_HAS_MESSAGES
53 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
60 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
64 /* Bus connection being communicated on */
67 /* Connection to AccountsService */
68 GDBusProxy *accounts_service_proxy;
69 GList *user_account_objects;
71 /* D-Bus signals for display manager events */
72 guint session_added_signal;
73 guint session_removed_signal;
75 /* File monitor for password file */
76 GFileMonitor *passwd_monitor;
78 /* TRUE if have scanned users */
84 /* List of sessions */
86 } LightDMUserListPrivate;
96 LightDMUserList *user_list;
100 gchar *home_directory;
103 gboolean has_messages;
109 } LightDMUserPrivate;
113 GObject parent_instance;
120 GObjectClass parent_class;
123 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
124 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
125 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
126 GType session_get_type (void);
127 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
129 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
130 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
132 #define PASSWD_FILE "/etc/passwd"
133 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
135 static LightDMUserList *singleton = NULL;
138 * lightdm_user_list_get_instance:
142 * Return value: (transfer none): the #LightDMUserList
145 lightdm_user_list_get_instance (void)
148 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
153 get_user_by_name (LightDMUserList *user_list, const gchar *username)
155 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
158 for (link = priv->users; link; link = link->next)
160 LightDMUser *user = link->data;
161 if (strcmp (lightdm_user_get_name (user), username) == 0)
169 compare_user (gconstpointer a, gconstpointer b)
171 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
172 return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
176 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
178 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
180 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
181 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
182 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
185 g_free (priv->real_name);
186 priv->real_name = g_strdup (real_name);
187 g_free (priv->home_directory);
188 priv->home_directory = g_strdup (home_directory);
189 g_free (priv->image);
190 priv->image = g_strdup (image);
196 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
198 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
202 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
204 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
208 gchar **hidden_users, **hidden_shells;
209 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
210 GError *error = NULL;
212 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
214 config = g_key_file_new ();
215 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
216 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
217 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
218 g_clear_error (&error);
220 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
221 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
225 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
227 value = g_strdup ("nobody nobody4 noaccess");
228 hidden_users = g_strsplit (value, " ", -1);
231 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
233 value = g_strdup ("/bin/false /usr/sbin/nologin");
234 hidden_shells = g_strsplit (value, " ", -1);
237 g_key_file_free (config);
243 struct passwd *entry;
245 LightDMUserPrivate *user_priv;
247 gchar *real_name, *image;
255 /* Ignore system users */
256 if (entry->pw_uid < minimum_uid)
259 /* Ignore users disabled by shell */
262 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
263 if (hidden_shells[i])
267 /* Ignore certain users */
268 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
272 tokens = g_strsplit (entry->pw_gecos, ",", -1);
273 if (tokens[0] != NULL && tokens[0][0] != '\0')
274 real_name = g_strdup (tokens[0]);
276 real_name = g_strdup ("");
279 image = g_build_filename (entry->pw_dir, ".face", NULL);
280 if (!g_file_test (image, G_FILE_TEST_EXISTS))
283 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
284 if (!g_file_test (image, G_FILE_TEST_EXISTS))
291 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
292 user_priv = GET_USER_PRIVATE (user);
293 user_priv->user_list = user_list;
294 g_free (user_priv->name);
295 user_priv->name = g_strdup (entry->pw_name);
296 g_free (user_priv->real_name);
297 user_priv->real_name = real_name;
298 g_free (user_priv->home_directory);
299 user_priv->home_directory = g_strdup (entry->pw_dir);
300 g_free (user_priv->image);
301 user_priv->image = image;
303 /* Update existing users if have them */
304 for (link = priv->users; link; link = link->next)
306 LightDMUser *info = link->data;
307 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
309 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
310 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
311 g_object_unref (user);
318 /* Only notify once we have loaded the user list */
319 if (priv->have_users)
320 new_users = g_list_insert_sorted (new_users, user, compare_user);
322 users = g_list_insert_sorted (users, user, compare_user);
324 g_strfreev (hidden_users);
325 g_strfreev (hidden_shells);
328 g_warning ("Failed to read password database: %s", strerror (errno));
332 /* Use new user list */
333 old_users = priv->users;
336 /* Notify of changes */
337 for (link = new_users; link; link = link->next)
339 LightDMUser *info = link->data;
340 g_debug ("User %s added", lightdm_user_get_name (info));
341 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
343 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
345 g_list_free (new_users);
346 for (link = changed_users; link; link = link->next)
348 LightDMUser *info = link->data;
349 g_debug ("User %s changed", lightdm_user_get_name (info));
350 g_signal_emit (info, user_signals[CHANGED], 0);
352 g_list_free (changed_users);
353 for (link = old_users; link; link = link->next)
357 /* See if this user is in the current list */
358 for (new_link = priv->users; new_link; new_link = new_link->next)
360 if (new_link->data == link->data)
366 LightDMUser *info = link->data;
367 g_debug ("User %s removed", lightdm_user_get_name (info));
368 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
369 g_object_unref (info);
372 g_list_free (old_users);
376 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
378 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
380 g_debug ("%s changed, reloading user list", g_file_get_path (file));
381 load_passwd_file (user_list, TRUE);
386 update_user (UserAccountObject *object)
388 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
389 GVariant *result, *value;
392 GError *error = NULL;
394 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
395 "org.freedesktop.Accounts",
396 g_dbus_proxy_get_object_path (object->proxy),
397 "org.freedesktop.DBus.Properties",
399 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
400 G_VARIANT_TYPE ("(a{sv})"),
401 G_DBUS_CALL_FLAGS_NONE,
406 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
407 g_clear_error (&error);
411 g_variant_get (result, "(a{sv})", &iter);
412 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
414 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
417 g_variant_get (value, "&s", &user_name);
419 priv->name = g_strdup (user_name);
421 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
424 g_variant_get (value, "&s", &real_name);
425 g_free (priv->real_name);
426 priv->real_name = g_strdup (real_name);
428 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
430 gchar *home_directory;
431 g_variant_get (value, "&s", &home_directory);
432 g_free (priv->home_directory);
433 priv->home_directory = g_strdup (home_directory);
435 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
438 g_variant_get (value, "&s", &icon_file);
439 g_free (priv->image);
440 if (strcmp (icon_file, "") == 0)
443 priv->image = g_strdup (icon_file);
445 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
447 gchar *background_file;
448 g_variant_get (value, "&s", &background_file);
449 g_free (priv->background);
450 if (strcmp (background_file, "") == 0)
451 priv->background = NULL;
453 priv->background = g_strdup (background_file);
456 g_variant_iter_free (iter);
458 g_variant_unref (result);
464 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
466 if (strcmp (signal_name, "Changed") == 0)
468 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
470 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
471 update_user (object);
472 g_signal_emit (object->user, user_signals[CHANGED], 0);
475 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
479 static UserAccountObject *
480 user_account_object_new (LightDMUserList *user_list, const gchar *path)
483 UserAccountObject *object;
484 GError *error = NULL;
486 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
487 G_DBUS_PROXY_FLAGS_NONE,
489 "org.freedesktop.Accounts",
491 "org.freedesktop.Accounts.User",
495 g_warning ("Error getting user %s: %s", path, error->message);
496 g_clear_error (&error);
500 object = g_malloc0 (sizeof (UserAccountObject));
501 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
502 GET_USER_PRIVATE (object->user)->user_list = user_list;
503 object->proxy = proxy;
504 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
510 user_account_object_free (UserAccountObject *object)
514 g_object_unref (object->user);
515 g_object_unref (object->proxy);
519 static UserAccountObject *
520 find_user_account_object (LightDMUserList *user_list, const gchar *path)
522 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
525 for (link = priv->user_account_objects; link; link = link->next)
527 UserAccountObject *object = link->data;
528 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
536 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
538 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
540 if (strcmp (signal_name, "UserAdded") == 0)
542 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
545 UserAccountObject *object;
547 g_variant_get (parameters, "(&o)", &path);
549 /* Ignore duplicate requests */
550 object = find_user_account_object (user_list, path);
554 object = user_account_object_new (user_list, path);
555 if (object && update_user (object))
557 g_debug ("User %s added", path);
558 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
559 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
560 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
561 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
564 user_account_object_free (object);
567 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
569 else if (strcmp (signal_name, "UserDeleted") == 0)
571 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
574 UserAccountObject *object;
576 g_variant_get (parameters, "(&o)", &path);
578 object = find_user_account_object (user_list, path);
582 g_debug ("User %s deleted", path);
583 priv->users = g_list_remove (priv->users, object->user);
584 g_object_unref (object->user);
586 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
588 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
589 user_account_object_free (object);
592 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
597 load_session (LightDMUserList *user_list, const gchar *path)
599 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
600 Session *session = NULL;
601 GVariant *result, *username;
602 GError *error = NULL;
604 result = g_dbus_connection_call_sync (priv->bus,
605 "org.freedesktop.DisplayManager",
607 "org.freedesktop.DBus.Properties",
609 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
610 G_VARIANT_TYPE ("(v)"),
611 G_DBUS_CALL_FLAGS_NONE,
616 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
617 g_clear_error (&error);
621 g_variant_get (result, "(v)", &username);
622 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
626 g_variant_get (username, "&s", &name);
628 g_debug ("Loaded session %s (%s)", path, name);
629 session = g_object_new (session_get_type (), NULL);
630 session->username = g_strdup (name);
631 session->path = g_strdup (path);
632 priv->sessions = g_list_append (priv->sessions, session);
634 g_variant_unref (username);
635 g_variant_unref (result);
641 session_added_cb (GDBusConnection *connection,
642 const gchar *sender_name,
643 const gchar *object_path,
644 const gchar *interface_name,
645 const gchar *signal_name,
646 GVariant *parameters,
649 LightDMUserList *user_list = data;
652 LightDMUser *user = NULL;
654 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
656 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
660 g_variant_get (parameters, "(&o)", &path);
661 session = load_session (user_list, path);
663 user = get_user_by_name (user_list, session->username);
665 g_signal_emit (user, user_signals[CHANGED], 0);
669 session_removed_cb (GDBusConnection *connection,
670 const gchar *sender_name,
671 const gchar *object_path,
672 const gchar *interface_name,
673 const gchar *signal_name,
674 GVariant *parameters,
677 LightDMUserList *user_list = data;
678 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
682 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
684 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
688 g_variant_get (parameters, "(&o)", &path);
690 for (link = priv->sessions; link; link = link->next)
692 Session *session = link->data;
693 if (strcmp (session->path, path) == 0)
697 g_debug ("Session %s removed", path);
698 priv->sessions = g_list_remove_link (priv->sessions, link);
699 user = get_user_by_name (user_list, session->username);
701 g_signal_emit (user, user_signals[CHANGED], 0);
702 g_object_unref (session);
709 update_users (LightDMUserList *user_list)
711 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
713 GError *error = NULL;
715 if (priv->have_users)
717 priv->have_users = TRUE;
719 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
720 G_DBUS_PROXY_FLAGS_NONE,
722 "org.freedesktop.Accounts",
723 "/org/freedesktop/Accounts",
724 "org.freedesktop.Accounts",
728 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
729 g_clear_error (&error);
731 /* Check if the service exists */
732 if (priv->accounts_service_proxy)
736 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
739 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
740 g_object_unref (priv->accounts_service_proxy);
741 priv->accounts_service_proxy = NULL;
746 if (priv->accounts_service_proxy)
750 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
752 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
754 g_variant_new ("()"),
755 G_DBUS_CALL_FLAGS_NONE,
760 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
761 g_clear_error (&error);
765 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
770 g_debug ("Loading users from org.freedesktop.Accounts");
771 g_variant_get (result, "(ao)", &iter);
772 while (g_variant_iter_loop (iter, "&o", &path))
774 UserAccountObject *object;
776 g_debug ("Loading user %s", path);
778 object = user_account_object_new (user_list, path);
779 if (object && update_user (object))
781 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
782 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
783 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
786 user_account_object_free (object);
788 g_variant_iter_free (iter);
791 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
793 g_variant_unref (result);
799 load_passwd_file (user_list, FALSE);
801 /* Watch for changes to user list */
803 passwd_file = g_file_new_for_path (PASSWD_FILE);
804 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
805 g_object_unref (passwd_file);
807 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
809 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
810 g_clear_error (&error);
813 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
814 "org.freedesktop.DisplayManager",
815 "org.freedesktop.DisplayManager",
817 "/org/freedesktop/DisplayManager",
819 G_DBUS_SIGNAL_FLAGS_NONE,
823 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
824 "org.freedesktop.DisplayManager",
825 "org.freedesktop.DisplayManager",
827 "/org/freedesktop/DisplayManager",
829 G_DBUS_SIGNAL_FLAGS_NONE,
833 result = g_dbus_connection_call_sync (priv->bus,
834 "org.freedesktop.DisplayManager",
835 "/org/freedesktop/DisplayManager",
836 "org.freedesktop.DBus.Properties",
838 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
839 G_VARIANT_TYPE ("(v)"),
840 G_DBUS_CALL_FLAGS_NONE,
845 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
846 g_clear_error (&error);
849 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
855 g_variant_get (result, "(v)", &value);
857 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
858 g_variant_get (value, "ao", &iter);
859 while (g_variant_iter_loop (iter, "&o", &path))
860 load_session (user_list, path);
861 g_variant_iter_free (iter);
863 g_variant_unref (value);
866 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
868 g_variant_unref (result);
873 * lightdm_user_list_get_length:
874 * @user_list: a #LightDMUserList
876 * Return value: The number of users able to log in
879 lightdm_user_list_get_length (LightDMUserList *user_list)
881 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
882 update_users (user_list);
883 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
887 * lightdm_user_list_get_users:
888 * @user_list: A #LightDMUserList
890 * Get a list of users to present to the user. This list may be a subset of the
891 * available users and may be empty depending on the server configuration.
893 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
896 lightdm_user_list_get_users (LightDMUserList *user_list)
898 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
899 update_users (user_list);
900 return GET_LIST_PRIVATE (user_list)->users;
904 * lightdm_user_list_get_user_by_name:
905 * @user_list: A #LightDMUserList
906 * @username: Name of user to get.
908 * Get infomation about a given user or #NULL if this user doesn't exist.
910 * Return value: (transfer none): A #LightDMUser entry for the given user.
913 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
915 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
916 g_return_val_if_fail (username != NULL, NULL);
918 update_users (user_list);
920 return get_user_by_name (user_list, username);
924 lightdm_user_list_init (LightDMUserList *user_list)
926 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
928 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
932 lightdm_user_list_set_property (GObject *object,
937 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
941 lightdm_user_list_get_property (GObject *object,
946 LightDMUserList *self;
948 self = LIGHTDM_USER_LIST (object);
952 case LIST_PROP_NUM_USERS:
953 g_value_set_int (value, lightdm_user_list_get_length (self));
956 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
962 lightdm_user_list_finalize (GObject *object)
964 LightDMUserList *self = LIGHTDM_USER_LIST (object);
965 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
967 if (priv->accounts_service_proxy)
968 g_object_unref (priv->accounts_service_proxy);
969 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
970 if (priv->passwd_monitor)
971 g_object_unref (priv->passwd_monitor);
972 g_list_free_full (priv->users, g_object_unref);
973 g_list_free_full (priv->sessions, g_object_unref);
974 if (priv->session_added_signal)
975 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
976 if (priv->session_removed_signal)
977 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
978 g_object_unref (priv->bus);
980 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
984 lightdm_user_list_class_init (LightDMUserListClass *klass)
986 GObjectClass *object_class = G_OBJECT_CLASS (klass);
988 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
990 object_class->set_property = lightdm_user_list_set_property;
991 object_class->get_property = lightdm_user_list_get_property;
992 object_class->finalize = lightdm_user_list_finalize;
994 g_object_class_install_property (object_class,
996 g_param_spec_int ("num-users",
998 "Number of login users",
1002 * LightDMUserList::user-added:
1003 * @user_list: A #LightDMUserList
1004 * @user: The #LightDM user that has been added.
1006 * The ::user-added signal gets emitted when a user account is created.
1008 list_signals[USER_ADDED] =
1009 g_signal_new ("user-added",
1010 G_TYPE_FROM_CLASS (klass),
1012 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
1015 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1018 * LightDMUserList::user-changed:
1019 * @user_list: A #LightDMUserList
1020 * @user: The #LightDM user that has been changed.
1022 * The ::user-changed signal gets emitted when a user account is modified.
1024 list_signals[USER_CHANGED] =
1025 g_signal_new ("user-changed",
1026 G_TYPE_FROM_CLASS (klass),
1028 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
1031 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1034 * LightDMUserList::user-removed:
1035 * @user_list: A #LightDMUserList
1036 * @user: The #LightDM user that has been removed.
1038 * The ::user-removed signal gets emitted when a user account is removed.
1040 list_signals[USER_REMOVED] =
1041 g_signal_new ("user-removed",
1042 G_TYPE_FROM_CLASS (klass),
1044 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1047 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1051 * lightdm_user_get_name:
1052 * @user: A #LightDMUser
1054 * Get the name of a user.
1056 * Return value: The name of the given user
1059 lightdm_user_get_name (LightDMUser *user)
1061 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1062 return GET_USER_PRIVATE (user)->name;
1066 * lightdm_user_get_real_name:
1067 * @user: A #LightDMUser
1069 * Get the real name of a user.
1071 * Return value: The real name of the given user
1074 lightdm_user_get_real_name (LightDMUser *user)
1076 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1077 return GET_USER_PRIVATE (user)->real_name;
1081 * lightdm_user_get_display_name:
1082 * @user: A #LightDMUser
1084 * Get the display name of a user.
1086 * Return value: The display name of the given user
1089 lightdm_user_get_display_name (LightDMUser *user)
1091 LightDMUserPrivate *priv;
1093 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1095 priv = GET_USER_PRIVATE (user);
1096 if (strcmp (priv->real_name, ""))
1097 return priv->real_name;
1103 * lightdm_user_get_home_directory:
1104 * @user: A #LightDMUser
1106 * Get the home directory for a user.
1108 * Return value: The users home directory
1111 lightdm_user_get_home_directory (LightDMUser *user)
1113 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1114 return GET_USER_PRIVATE (user)->home_directory;
1118 * lightdm_user_get_image:
1119 * @user: A #LightDMUser
1121 * Get the image URI for a user.
1123 * Return value: The image URI for the given user or #NULL if no URI
1126 lightdm_user_get_image (LightDMUser *user)
1128 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1129 return GET_USER_PRIVATE (user)->image;
1133 * lightdm_user_get_background:
1134 * @user: A #LightDMUser
1136 * Get the background file path for a user.
1138 * Return value: The background file path for the given user or #NULL if no path
1141 lightdm_user_get_background (LightDMUser *user)
1143 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1144 return GET_USER_PRIVATE (user)->background;
1148 load_dmrc (LightDMUser *user)
1150 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1152 //gboolean have_dmrc;
1154 if (!priv->dmrc_file)
1155 priv->dmrc_file = g_key_file_new ();
1157 /* Load from the user directory */
1158 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1159 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1162 /* If no ~/.dmrc, then load from the cache */
1165 // FIXME: Watch for changes
1167 /* The Language field contains the locale */
1169 g_free (priv->language);
1170 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1174 g_strfreev (priv->layouts);
1175 priv->layouts = NULL;
1177 if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1179 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1180 priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1181 priv->layouts[1] = NULL;
1185 g_free (priv->session);
1186 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1190 get_property (GDBusProxy *proxy, const gchar *property)
1197 answer = g_dbus_proxy_get_cached_property (proxy, property);
1201 g_warning ("Could not get accounts property %s", property);
1209 get_boolean_property (GDBusProxy *proxy, const gchar *property)
1214 answer = get_property (proxy, property);
1215 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN))
1217 g_warning ("Unexpected accounts property type for %s: %s",
1218 property, g_variant_get_type_string (answer));
1219 g_variant_unref (answer);
1223 rv = g_variant_get_boolean (answer);
1224 g_variant_unref (answer);
1230 get_string_property (GDBusProxy *proxy, const gchar *property)
1235 answer = get_property (proxy, property);
1236 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING))
1238 g_warning ("Unexpected accounts property type for %s: %s",
1239 property, g_variant_get_type_string (answer));
1240 g_variant_unref (answer);
1244 rv = g_strdup (g_variant_get_string (answer, NULL));
1245 if (strcmp (rv, "") == 0)
1250 g_variant_unref (answer);
1256 get_string_array_property (GDBusProxy *proxy, const gchar *property)
1264 answer = g_dbus_proxy_get_cached_property (proxy, property);
1268 g_warning ("Could not get accounts property %s", property);
1272 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
1274 g_warning ("Unexpected accounts property type for %s: %s",
1275 property, g_variant_get_type_string (answer));
1276 g_variant_unref (answer);
1280 rv = g_variant_dup_strv (answer, NULL);
1282 g_variant_unref (answer);
1287 load_accounts_service (LightDMUser *user)
1289 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1290 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1291 UserAccountObject *account = NULL;
1295 /* First, find AccountObject proxy */
1296 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1298 UserAccountObject *a = iter->data;
1299 if (a->user == user)
1308 /* We have proxy, let's grab some properties */
1310 g_free (priv->language);
1311 priv->language = get_string_property (account->proxy, "Language");
1313 g_free (priv->session);
1314 priv->session = get_string_property (account->proxy, "XSession");
1316 value = get_string_array_property (account->proxy, "XKeyboardLayouts");
1321 g_strfreev (priv->layouts);
1322 priv->layouts = value;
1328 priv->has_messages = get_boolean_property (account->proxy, "XHasMessages");
1333 /* Loads language/layout/session info for user */
1335 load_user_values (LightDMUser *user)
1337 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1340 load_accounts_service (user); // overrides dmrc values
1342 /* Ensure a few guarantees */
1343 if (priv->layouts == NULL)
1345 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1346 priv->layouts[0] = NULL;
1351 * lightdm_user_get_language:
1352 * @user: A #LightDMUser
1354 * Get the language for a user.
1356 * Return value: The language in the form of a local specification (e.g. "de_DE.UTF-8") for the given user or #NULL if using the system default locale.
1359 lightdm_user_get_language (LightDMUser *user)
1361 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1362 load_user_values (user);
1363 return GET_USER_PRIVATE (user)->language;
1367 * lightdm_user_get_layout:
1368 * @user: A #LightDMUser
1370 * Get the keyboard layout for a user.
1372 * 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.
1375 lightdm_user_get_layout (LightDMUser *user)
1377 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1378 load_user_values (user);
1379 return GET_USER_PRIVATE (user)->layouts[0];
1383 * lightdm_user_get_layouts:
1384 * @user: A #LightDMUser
1386 * Get the configured keyboard layouts for a user.
1388 * 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.
1390 const gchar * const *
1391 lightdm_user_get_layouts (LightDMUser *user)
1393 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1394 load_user_values (user);
1395 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1399 * lightdm_user_get_session:
1400 * @user: A #LightDMUser
1402 * Get the session for a user.
1404 * Return value: The session for the given user or #NULL if using system defaults.
1407 lightdm_user_get_session (LightDMUser *user)
1409 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1410 load_user_values (user);
1411 return GET_USER_PRIVATE (user)->session;
1415 * lightdm_user_get_logged_in:
1416 * @user: A #LightDMUser
1418 * Check if a user is logged in.
1420 * Return value: #TRUE if the user is currently logged in.
1423 lightdm_user_get_logged_in (LightDMUser *user)
1425 LightDMUserPrivate *priv;
1426 LightDMUserListPrivate *list_priv;
1429 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1431 priv = GET_USER_PRIVATE (user);
1432 list_priv = GET_LIST_PRIVATE (priv->user_list);
1434 for (link = list_priv->sessions; link; link = link->next)
1436 Session *session = link->data;
1437 if (strcmp (session->username, priv->name) == 0)
1445 * lightdm_user_get_has_messages:
1446 * @user: A #LightDMUser
1448 * Check if a user has waiting messages.
1450 * Return value: #TRUE if the user has waiting messages.
1453 lightdm_user_get_has_messages (LightDMUser *user)
1455 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1456 load_user_values (user);
1457 return GET_USER_PRIVATE (user)->has_messages;
1461 lightdm_user_init (LightDMUser *user)
1466 lightdm_user_set_property (GObject *object,
1468 const GValue *value,
1471 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1475 lightdm_user_get_property (GObject *object,
1482 self = LIGHTDM_USER (object);
1486 case USER_PROP_NAME:
1487 g_value_set_string (value, lightdm_user_get_name (self));
1489 case USER_PROP_REAL_NAME:
1490 g_value_set_string (value, lightdm_user_get_real_name (self));
1492 case USER_PROP_DISPLAY_NAME:
1493 g_value_set_string (value, lightdm_user_get_display_name (self));
1495 case USER_PROP_HOME_DIRECTORY:
1496 g_value_set_string (value, lightdm_user_get_home_directory (self));
1498 case USER_PROP_IMAGE:
1499 g_value_set_string (value, lightdm_user_get_image (self));
1501 case USER_PROP_BACKGROUND:
1502 g_value_set_string (value, lightdm_user_get_background (self));
1504 case USER_PROP_LANGUAGE:
1505 g_value_set_string (value, lightdm_user_get_language (self));
1507 case USER_PROP_LAYOUT:
1508 g_value_set_string (value, lightdm_user_get_layout (self));
1510 case USER_PROP_LAYOUTS:
1511 g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
1513 case USER_PROP_SESSION:
1514 g_value_set_string (value, lightdm_user_get_session (self));
1516 case USER_PROP_LOGGED_IN:
1517 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1519 case USER_PROP_HAS_MESSAGES:
1520 g_value_set_boolean (value, lightdm_user_get_has_messages (self));
1523 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1529 lightdm_user_finalize (GObject *object)
1531 LightDMUser *self = LIGHTDM_USER (object);
1532 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1534 g_free (priv->name);
1535 g_free (priv->real_name);
1536 g_free (priv->home_directory);
1537 g_free (priv->image);
1538 g_free (priv->background);
1539 g_strfreev (priv->layouts);
1540 if (priv->dmrc_file)
1541 g_key_file_free (priv->dmrc_file);
1545 lightdm_user_class_init (LightDMUserClass *klass)
1547 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1549 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1551 object_class->set_property = lightdm_user_set_property;
1552 object_class->get_property = lightdm_user_get_property;
1553 object_class->finalize = lightdm_user_finalize;
1555 g_object_class_install_property (object_class,
1557 g_param_spec_string ("name",
1561 G_PARAM_READWRITE));
1562 g_object_class_install_property (object_class,
1563 USER_PROP_REAL_NAME,
1564 g_param_spec_string ("real-name",
1568 G_PARAM_READWRITE));
1569 g_object_class_install_property (object_class,
1570 USER_PROP_DISPLAY_NAME,
1571 g_param_spec_string ("display-name",
1573 "Users display name",
1576 g_object_class_install_property (object_class,
1577 USER_PROP_HOME_DIRECTORY,
1578 g_param_spec_string ("home-directory",
1582 G_PARAM_READWRITE));
1583 g_object_class_install_property (object_class,
1585 g_param_spec_string ("image",
1589 G_PARAM_READWRITE));
1590 g_object_class_install_property (object_class,
1591 USER_PROP_BACKGROUND,
1592 g_param_spec_string ("background",
1596 G_PARAM_READWRITE));
1597 g_object_class_install_property (object_class,
1599 g_param_spec_string ("language",
1601 "Language used by this user",
1604 g_object_class_install_property (object_class,
1606 g_param_spec_string ("layout",
1608 "Keyboard layout used by this user",
1611 g_object_class_install_property (object_class,
1613 g_param_spec_boxed ("layouts",
1615 "Keyboard layouts used by this user",
1618 g_object_class_install_property (object_class,
1620 g_param_spec_string ("session",
1622 "Session used by this user",
1625 g_object_class_install_property (object_class,
1626 USER_PROP_LOGGED_IN,
1627 g_param_spec_boolean ("logged-in",
1629 "TRUE if the user is currently in a session",
1631 G_PARAM_READWRITE));
1632 g_object_class_install_property (object_class,
1633 USER_PROP_LOGGED_IN,
1634 g_param_spec_boolean ("has-messages",
1636 "TRUE if the user is has waiting messages",
1638 G_PARAM_READWRITE));
1641 * LightDMUser::changed:
1642 * @user: A #LightDMUser
1644 * The ::changed signal gets emitted this user account is modified.
1646 user_signals[CHANGED] =
1647 g_signal_new ("changed",
1648 G_TYPE_FROM_CLASS (klass),
1650 G_STRUCT_OFFSET (LightDMUserClass, changed),
1657 session_init (Session *session)
1662 session_finalize (GObject *object)
1664 Session *self = SESSION (object);
1666 g_free (self->path);
1667 g_free (self->username);
1671 session_class_init (SessionClass *klass)
1673 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1674 object_class->finalize = session_finalize;