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 /* User list this user is part of */
97 LightDMUserList *user_list;
105 /* Descriptive name for user */
108 /* Home directory of user */
109 gchar *home_directory;
114 /* Background image for users */
117 /* TRUE if this user has messages available */
118 gboolean has_messages;
120 /* User chosen language */
123 /* User layout preferences */
126 /* User default session */
128 } LightDMUserPrivate;
132 GObject parent_instance;
139 GObjectClass parent_class;
142 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
143 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
144 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
145 GType session_get_type (void);
146 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
148 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
149 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
151 #define PASSWD_FILE "/etc/passwd"
152 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
154 static LightDMUserList *singleton = NULL;
157 * lightdm_user_list_get_instance:
161 * Return value: (transfer none): the #LightDMUserList
164 lightdm_user_list_get_instance (void)
167 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
172 get_user_by_name (LightDMUserList *user_list, const gchar *username)
174 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
177 for (link = priv->users; link; link = link->next)
179 LightDMUser *user = link->data;
180 if (g_strcmp0 (lightdm_user_get_name (user), username) == 0)
188 compare_user (gconstpointer a, gconstpointer b)
190 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
191 return g_strcmp0 (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
195 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
197 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
199 /* Skip if already set to this */
200 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
201 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
202 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
205 g_free (priv->real_name);
206 priv->real_name = g_strdup (real_name);
207 g_free (priv->home_directory);
208 priv->home_directory = g_strdup (home_directory);
209 g_free (priv->image);
210 priv->image = g_strdup (image);
216 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
218 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
222 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
224 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
228 gchar **hidden_users, **hidden_shells;
229 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
230 GError *error = NULL;
232 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
234 config = g_key_file_new ();
235 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
236 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
237 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
238 g_clear_error (&error);
240 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
241 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
245 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
247 value = g_strdup ("nobody nobody4 noaccess");
248 hidden_users = g_strsplit (value, " ", -1);
251 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
253 value = g_strdup ("/bin/false /usr/sbin/nologin");
254 hidden_shells = g_strsplit (value, " ", -1);
257 g_key_file_free (config);
263 struct passwd *entry;
265 LightDMUserPrivate *user_priv;
267 gchar *real_name, *image;
275 /* Ignore system users */
276 if (entry->pw_uid < minimum_uid)
279 /* Ignore users disabled by shell */
282 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
283 if (hidden_shells[i])
287 /* Ignore certain users */
288 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
292 tokens = g_strsplit (entry->pw_gecos, ",", -1);
293 if (tokens[0] != NULL && tokens[0][0] != '\0')
294 real_name = g_strdup (tokens[0]);
296 real_name = g_strdup ("");
299 image = g_build_filename (entry->pw_dir, ".face", NULL);
300 if (!g_file_test (image, G_FILE_TEST_EXISTS))
303 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
304 if (!g_file_test (image, G_FILE_TEST_EXISTS))
311 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
312 user_priv = GET_USER_PRIVATE (user);
313 user_priv->user_list = user_list;
314 g_free (user_priv->name);
315 user_priv->name = g_strdup (entry->pw_name);
316 g_free (user_priv->real_name);
317 user_priv->real_name = real_name;
318 g_free (user_priv->home_directory);
319 user_priv->home_directory = g_strdup (entry->pw_dir);
320 g_free (user_priv->image);
321 user_priv->image = image;
323 /* Update existing users if have them */
324 for (link = priv->users; link; link = link->next)
326 LightDMUser *info = link->data;
327 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
329 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
330 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
331 g_object_unref (user);
338 /* Only notify once we have loaded the user list */
339 if (priv->have_users)
340 new_users = g_list_insert_sorted (new_users, user, compare_user);
342 users = g_list_insert_sorted (users, user, compare_user);
344 g_strfreev (hidden_users);
345 g_strfreev (hidden_shells);
348 g_warning ("Failed to read password database: %s", strerror (errno));
352 /* Use new user list */
353 old_users = priv->users;
356 /* Notify of changes */
357 for (link = new_users; link; link = link->next)
359 LightDMUser *info = link->data;
360 g_debug ("User %s added", lightdm_user_get_name (info));
361 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
363 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
365 g_list_free (new_users);
366 for (link = changed_users; link; link = link->next)
368 LightDMUser *info = link->data;
369 g_debug ("User %s changed", lightdm_user_get_name (info));
370 g_signal_emit (info, user_signals[CHANGED], 0);
372 g_list_free (changed_users);
373 for (link = old_users; link; link = link->next)
377 /* See if this user is in the current list */
378 for (new_link = priv->users; new_link; new_link = new_link->next)
380 if (new_link->data == link->data)
386 LightDMUser *info = link->data;
387 g_debug ("User %s removed", lightdm_user_get_name (info));
388 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
389 g_object_unref (info);
392 g_list_free (old_users);
396 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
398 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
400 g_debug ("%s changed, reloading user list", g_file_get_path (file));
401 load_passwd_file (user_list, TRUE);
406 update_user (UserAccountObject *object)
408 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
409 GVariant *result, *value;
412 GError *error = NULL;
414 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
415 "org.freedesktop.Accounts",
416 g_dbus_proxy_get_object_path (object->proxy),
417 "org.freedesktop.DBus.Properties",
419 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
420 G_VARIANT_TYPE ("(a{sv})"),
421 G_DBUS_CALL_FLAGS_NONE,
426 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
427 g_clear_error (&error);
431 /* Store the properties we need */
432 g_variant_get (result, "(a{sv})", &iter);
433 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
435 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
438 priv->name = g_variant_dup_string (value, NULL);
440 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
442 g_free (priv->real_name);
443 priv->real_name = g_variant_dup_string (value, NULL);
445 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
447 g_free (priv->home_directory);
448 priv->home_directory = g_variant_dup_string (value, NULL);
450 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
452 g_free (priv->image);
453 priv->image = g_variant_dup_string (value, NULL);
454 if (strcmp (priv->image, "") == 0)
456 g_free (priv->image);
460 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
462 g_free (priv->background);
463 priv->background = g_variant_dup_string (value, NULL);
464 if (strcmp (priv->background, "") == 0)
466 g_free (priv->background);
467 priv->background = NULL;
470 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
471 priv->has_messages = g_variant_get_boolean (value);
473 g_variant_iter_free (iter);
475 g_variant_unref (result);
481 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
483 if (strcmp (signal_name, "Changed") == 0)
485 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
487 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
488 update_user (object);
489 g_signal_emit (object->user, user_signals[CHANGED], 0);
492 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
496 static UserAccountObject *
497 user_account_object_new (LightDMUserList *user_list, const gchar *path)
500 UserAccountObject *object;
501 GError *error = NULL;
503 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
504 G_DBUS_PROXY_FLAGS_NONE,
506 "org.freedesktop.Accounts",
508 "org.freedesktop.Accounts.User",
512 g_warning ("Error getting user %s: %s", path, error->message);
513 g_clear_error (&error);
517 object = g_malloc0 (sizeof (UserAccountObject));
518 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
519 GET_USER_PRIVATE (object->user)->user_list = user_list;
520 object->proxy = proxy;
521 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
527 user_account_object_free (UserAccountObject *object)
531 g_object_unref (object->user);
532 g_object_unref (object->proxy);
536 static UserAccountObject *
537 find_user_account_object (LightDMUserList *user_list, const gchar *path)
539 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
542 for (link = priv->user_account_objects; link; link = link->next)
544 UserAccountObject *object = link->data;
545 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
553 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
555 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
557 if (strcmp (signal_name, "UserAdded") == 0)
559 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
562 UserAccountObject *object;
564 g_variant_get (parameters, "(&o)", &path);
566 /* Ignore duplicate requests */
567 object = find_user_account_object (user_list, path);
571 object = user_account_object_new (user_list, path);
572 if (object && update_user (object))
574 g_debug ("User %s added", path);
575 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
576 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
577 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
578 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
581 user_account_object_free (object);
584 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
586 else if (strcmp (signal_name, "UserDeleted") == 0)
588 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
591 UserAccountObject *object;
593 g_variant_get (parameters, "(&o)", &path);
595 object = find_user_account_object (user_list, path);
599 g_debug ("User %s deleted", path);
600 priv->users = g_list_remove (priv->users, object->user);
601 g_object_unref (object->user);
603 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
605 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
606 user_account_object_free (object);
609 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
614 load_session (LightDMUserList *user_list, const gchar *path)
616 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
617 Session *session = NULL;
618 GVariant *result, *username;
619 GError *error = NULL;
621 result = g_dbus_connection_call_sync (priv->bus,
622 "org.freedesktop.DisplayManager",
624 "org.freedesktop.DBus.Properties",
626 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
627 G_VARIANT_TYPE ("(v)"),
628 G_DBUS_CALL_FLAGS_NONE,
633 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
634 g_clear_error (&error);
638 g_variant_get (result, "(v)", &username);
639 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
643 g_variant_get (username, "&s", &name);
645 g_debug ("Loaded session %s (%s)", path, name);
646 session = g_object_new (session_get_type (), NULL);
647 session->username = g_strdup (name);
648 session->path = g_strdup (path);
649 priv->sessions = g_list_append (priv->sessions, session);
651 g_variant_unref (username);
652 g_variant_unref (result);
658 session_added_cb (GDBusConnection *connection,
659 const gchar *sender_name,
660 const gchar *object_path,
661 const gchar *interface_name,
662 const gchar *signal_name,
663 GVariant *parameters,
666 LightDMUserList *user_list = data;
669 LightDMUser *user = NULL;
671 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
673 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
677 g_variant_get (parameters, "(&o)", &path);
678 session = load_session (user_list, path);
680 user = get_user_by_name (user_list, session->username);
682 g_signal_emit (user, user_signals[CHANGED], 0);
686 session_removed_cb (GDBusConnection *connection,
687 const gchar *sender_name,
688 const gchar *object_path,
689 const gchar *interface_name,
690 const gchar *signal_name,
691 GVariant *parameters,
694 LightDMUserList *user_list = data;
695 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
699 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
701 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
705 g_variant_get (parameters, "(&o)", &path);
707 for (link = priv->sessions; link; link = link->next)
709 Session *session = link->data;
710 if (strcmp (session->path, path) == 0)
714 g_debug ("Session %s removed", path);
715 priv->sessions = g_list_remove_link (priv->sessions, link);
716 user = get_user_by_name (user_list, session->username);
718 g_signal_emit (user, user_signals[CHANGED], 0);
719 g_object_unref (session);
726 update_users (LightDMUserList *user_list)
728 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
730 GError *error = NULL;
732 if (priv->have_users)
734 priv->have_users = TRUE;
736 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
737 G_DBUS_PROXY_FLAGS_NONE,
739 "org.freedesktop.Accounts",
740 "/org/freedesktop/Accounts",
741 "org.freedesktop.Accounts",
745 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
746 g_clear_error (&error);
748 /* Check if the service exists */
749 if (priv->accounts_service_proxy)
753 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
756 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
757 g_object_unref (priv->accounts_service_proxy);
758 priv->accounts_service_proxy = NULL;
763 if (priv->accounts_service_proxy)
767 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
769 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
771 g_variant_new ("()"),
772 G_DBUS_CALL_FLAGS_NONE,
777 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
778 g_clear_error (&error);
782 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
787 g_debug ("Loading users from org.freedesktop.Accounts");
788 g_variant_get (result, "(ao)", &iter);
789 while (g_variant_iter_loop (iter, "&o", &path))
791 UserAccountObject *object;
793 g_debug ("Loading user %s", path);
795 object = user_account_object_new (user_list, path);
796 if (object && update_user (object))
798 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
799 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
800 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
803 user_account_object_free (object);
805 g_variant_iter_free (iter);
808 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
810 g_variant_unref (result);
816 load_passwd_file (user_list, FALSE);
818 /* Watch for changes to user list */
820 passwd_file = g_file_new_for_path (PASSWD_FILE);
821 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
822 g_object_unref (passwd_file);
824 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
826 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
827 g_clear_error (&error);
830 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
831 "org.freedesktop.DisplayManager",
832 "org.freedesktop.DisplayManager",
834 "/org/freedesktop/DisplayManager",
836 G_DBUS_SIGNAL_FLAGS_NONE,
840 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
841 "org.freedesktop.DisplayManager",
842 "org.freedesktop.DisplayManager",
844 "/org/freedesktop/DisplayManager",
846 G_DBUS_SIGNAL_FLAGS_NONE,
850 result = g_dbus_connection_call_sync (priv->bus,
851 "org.freedesktop.DisplayManager",
852 "/org/freedesktop/DisplayManager",
853 "org.freedesktop.DBus.Properties",
855 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
856 G_VARIANT_TYPE ("(v)"),
857 G_DBUS_CALL_FLAGS_NONE,
862 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
863 g_clear_error (&error);
866 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
872 g_variant_get (result, "(v)", &value);
874 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
875 g_variant_get (value, "ao", &iter);
876 while (g_variant_iter_loop (iter, "&o", &path))
877 load_session (user_list, path);
878 g_variant_iter_free (iter);
880 g_variant_unref (value);
883 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
885 g_variant_unref (result);
890 * lightdm_user_list_get_length:
891 * @user_list: a #LightDMUserList
893 * Return value: The number of users able to log in
896 lightdm_user_list_get_length (LightDMUserList *user_list)
898 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
899 update_users (user_list);
900 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
904 * lightdm_user_list_get_users:
905 * @user_list: A #LightDMUserList
907 * Get a list of users to present to the user. This list may be a subset of the
908 * available users and may be empty depending on the server configuration.
910 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
913 lightdm_user_list_get_users (LightDMUserList *user_list)
915 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
916 update_users (user_list);
917 return GET_LIST_PRIVATE (user_list)->users;
921 * lightdm_user_list_get_user_by_name:
922 * @user_list: A #LightDMUserList
923 * @username: Name of user to get.
925 * Get infomation about a given user or #NULL if this user doesn't exist.
927 * Return value: (transfer none): A #LightDMUser entry for the given user.
930 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
932 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
933 g_return_val_if_fail (username != NULL, NULL);
935 update_users (user_list);
937 return get_user_by_name (user_list, username);
941 lightdm_user_list_init (LightDMUserList *user_list)
943 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
945 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
949 lightdm_user_list_set_property (GObject *object,
954 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
958 lightdm_user_list_get_property (GObject *object,
963 LightDMUserList *self;
965 self = LIGHTDM_USER_LIST (object);
969 case LIST_PROP_NUM_USERS:
970 g_value_set_int (value, lightdm_user_list_get_length (self));
973 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
979 lightdm_user_list_finalize (GObject *object)
981 LightDMUserList *self = LIGHTDM_USER_LIST (object);
982 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
984 if (priv->accounts_service_proxy)
985 g_object_unref (priv->accounts_service_proxy);
986 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
987 if (priv->passwd_monitor)
988 g_object_unref (priv->passwd_monitor);
989 g_list_free_full (priv->users, g_object_unref);
990 g_list_free_full (priv->sessions, g_object_unref);
991 if (priv->session_added_signal)
992 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
993 if (priv->session_removed_signal)
994 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
995 g_object_unref (priv->bus);
997 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
1001 lightdm_user_list_class_init (LightDMUserListClass *klass)
1003 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1005 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
1007 object_class->set_property = lightdm_user_list_set_property;
1008 object_class->get_property = lightdm_user_list_get_property;
1009 object_class->finalize = lightdm_user_list_finalize;
1011 g_object_class_install_property (object_class,
1012 LIST_PROP_NUM_USERS,
1013 g_param_spec_int ("num-users",
1015 "Number of login users",
1019 * LightDMUserList::user-added:
1020 * @user_list: A #LightDMUserList
1021 * @user: The #LightDM user that has been added.
1023 * The ::user-added signal gets emitted when a user account is created.
1025 list_signals[USER_ADDED] =
1026 g_signal_new ("user-added",
1027 G_TYPE_FROM_CLASS (klass),
1029 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
1032 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1035 * LightDMUserList::user-changed:
1036 * @user_list: A #LightDMUserList
1037 * @user: The #LightDM user that has been changed.
1039 * The ::user-changed signal gets emitted when a user account is modified.
1041 list_signals[USER_CHANGED] =
1042 g_signal_new ("user-changed",
1043 G_TYPE_FROM_CLASS (klass),
1045 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
1048 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1051 * LightDMUserList::user-removed:
1052 * @user_list: A #LightDMUserList
1053 * @user: The #LightDM user that has been removed.
1055 * The ::user-removed signal gets emitted when a user account is removed.
1057 list_signals[USER_REMOVED] =
1058 g_signal_new ("user-removed",
1059 G_TYPE_FROM_CLASS (klass),
1061 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1064 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1068 * lightdm_user_get_name:
1069 * @user: A #LightDMUser
1071 * Get the name of a user.
1073 * Return value: The name of the given user
1076 lightdm_user_get_name (LightDMUser *user)
1078 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1079 return GET_USER_PRIVATE (user)->name;
1083 * lightdm_user_get_real_name:
1084 * @user: A #LightDMUser
1086 * Get the real name of a user.
1088 * Return value: The real name of the given user
1091 lightdm_user_get_real_name (LightDMUser *user)
1093 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1094 return GET_USER_PRIVATE (user)->real_name;
1098 * lightdm_user_get_display_name:
1099 * @user: A #LightDMUser
1101 * Get the display name of a user.
1103 * Return value: The display name of the given user
1106 lightdm_user_get_display_name (LightDMUser *user)
1108 LightDMUserPrivate *priv;
1110 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1112 priv = GET_USER_PRIVATE (user);
1113 if (strcmp (priv->real_name, ""))
1114 return priv->real_name;
1120 * lightdm_user_get_home_directory:
1121 * @user: A #LightDMUser
1123 * Get the home directory for a user.
1125 * Return value: The users home directory
1128 lightdm_user_get_home_directory (LightDMUser *user)
1130 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1131 return GET_USER_PRIVATE (user)->home_directory;
1135 * lightdm_user_get_image:
1136 * @user: A #LightDMUser
1138 * Get the image URI for a user.
1140 * Return value: The image URI for the given user or #NULL if no URI
1143 lightdm_user_get_image (LightDMUser *user)
1145 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1146 return GET_USER_PRIVATE (user)->image;
1150 * lightdm_user_get_background:
1151 * @user: A #LightDMUser
1153 * Get the background file path for a user.
1155 * Return value: The background file path for the given user or #NULL if no path
1158 lightdm_user_get_background (LightDMUser *user)
1160 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1161 return GET_USER_PRIVATE (user)->background;
1165 load_dmrc (LightDMUser *user)
1167 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1169 //gboolean have_dmrc;
1171 if (!priv->dmrc_file)
1172 priv->dmrc_file = g_key_file_new ();
1174 /* Load from the user directory */
1175 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1176 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1179 /* If no ~/.dmrc, then load from the cache */
1182 // FIXME: Watch for changes
1184 /* The Language field contains the locale */
1186 g_free (priv->language);
1187 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1191 g_strfreev (priv->layouts);
1192 priv->layouts = NULL;
1194 if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1196 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1197 priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1198 priv->layouts[1] = NULL;
1202 g_free (priv->session);
1203 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1207 get_property (GDBusProxy *proxy, const gchar *property)
1214 answer = g_dbus_proxy_get_cached_property (proxy, property);
1218 g_warning ("Could not get accounts property %s", property);
1226 get_boolean_property (GDBusProxy *proxy, const gchar *property)
1231 answer = get_property (proxy, property);
1232 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN))
1234 g_warning ("Unexpected accounts property type for %s: %s",
1235 property, g_variant_get_type_string (answer));
1236 g_variant_unref (answer);
1240 rv = g_variant_get_boolean (answer);
1241 g_variant_unref (answer);
1247 get_string_property (GDBusProxy *proxy, const gchar *property)
1252 answer = get_property (proxy, property);
1253 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING))
1255 g_warning ("Unexpected accounts property type for %s: %s",
1256 property, g_variant_get_type_string (answer));
1257 g_variant_unref (answer);
1261 rv = g_strdup (g_variant_get_string (answer, NULL));
1262 if (strcmp (rv, "") == 0)
1267 g_variant_unref (answer);
1273 get_string_array_property (GDBusProxy *proxy, const gchar *property)
1281 answer = g_dbus_proxy_get_cached_property (proxy, property);
1285 g_warning ("Could not get accounts property %s", property);
1289 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
1291 g_warning ("Unexpected accounts property type for %s: %s",
1292 property, g_variant_get_type_string (answer));
1293 g_variant_unref (answer);
1297 rv = g_variant_dup_strv (answer, NULL);
1299 g_variant_unref (answer);
1304 load_accounts_service (LightDMUser *user)
1306 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1307 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1308 UserAccountObject *account = NULL;
1312 /* First, find AccountObject proxy */
1313 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1315 UserAccountObject *a = iter->data;
1316 if (a->user == user)
1325 /* We have proxy, let's grab some properties */
1327 g_free (priv->language);
1328 priv->language = get_string_property (account->proxy, "Language");
1330 g_free (priv->session);
1331 priv->session = get_string_property (account->proxy, "XSession");
1333 value = get_string_array_property (account->proxy, "XKeyboardLayouts");
1338 g_strfreev (priv->layouts);
1339 priv->layouts = value;
1345 priv->has_messages = get_boolean_property (account->proxy, "XHasMessages");
1350 /* Loads language/layout/session info for user */
1352 load_user_values (LightDMUser *user)
1354 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1357 load_accounts_service (user); // overrides dmrc values
1359 /* Ensure a few guarantees */
1360 if (priv->layouts == NULL)
1362 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1363 priv->layouts[0] = NULL;
1368 * lightdm_user_get_language:
1369 * @user: A #LightDMUser
1371 * Get the language for a user.
1373 * 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.
1376 lightdm_user_get_language (LightDMUser *user)
1378 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1379 load_user_values (user);
1380 return GET_USER_PRIVATE (user)->language;
1384 * lightdm_user_get_layout:
1385 * @user: A #LightDMUser
1387 * Get the keyboard layout for a user.
1389 * 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.
1392 lightdm_user_get_layout (LightDMUser *user)
1394 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1395 load_user_values (user);
1396 return GET_USER_PRIVATE (user)->layouts[0];
1400 * lightdm_user_get_layouts:
1401 * @user: A #LightDMUser
1403 * Get the configured keyboard layouts for a user.
1405 * 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.
1407 const gchar * const *
1408 lightdm_user_get_layouts (LightDMUser *user)
1410 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1411 load_user_values (user);
1412 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1416 * lightdm_user_get_session:
1417 * @user: A #LightDMUser
1419 * Get the session for a user.
1421 * Return value: The session for the given user or #NULL if using system defaults.
1424 lightdm_user_get_session (LightDMUser *user)
1426 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1427 load_user_values (user);
1428 return GET_USER_PRIVATE (user)->session;
1432 * lightdm_user_get_logged_in:
1433 * @user: A #LightDMUser
1435 * Check if a user is logged in.
1437 * Return value: #TRUE if the user is currently logged in.
1440 lightdm_user_get_logged_in (LightDMUser *user)
1442 LightDMUserPrivate *priv;
1443 LightDMUserListPrivate *list_priv;
1446 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1448 priv = GET_USER_PRIVATE (user);
1449 list_priv = GET_LIST_PRIVATE (priv->user_list);
1451 for (link = list_priv->sessions; link; link = link->next)
1453 Session *session = link->data;
1454 if (strcmp (session->username, priv->name) == 0)
1462 * lightdm_user_get_has_messages:
1463 * @user: A #LightDMUser
1465 * Check if a user has waiting messages.
1467 * Return value: #TRUE if the user has waiting messages.
1470 lightdm_user_get_has_messages (LightDMUser *user)
1472 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1473 load_user_values (user);
1474 return GET_USER_PRIVATE (user)->has_messages;
1478 lightdm_user_init (LightDMUser *user)
1483 lightdm_user_set_property (GObject *object,
1485 const GValue *value,
1488 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1492 lightdm_user_get_property (GObject *object,
1499 self = LIGHTDM_USER (object);
1503 case USER_PROP_NAME:
1504 g_value_set_string (value, lightdm_user_get_name (self));
1506 case USER_PROP_REAL_NAME:
1507 g_value_set_string (value, lightdm_user_get_real_name (self));
1509 case USER_PROP_DISPLAY_NAME:
1510 g_value_set_string (value, lightdm_user_get_display_name (self));
1512 case USER_PROP_HOME_DIRECTORY:
1513 g_value_set_string (value, lightdm_user_get_home_directory (self));
1515 case USER_PROP_IMAGE:
1516 g_value_set_string (value, lightdm_user_get_image (self));
1518 case USER_PROP_BACKGROUND:
1519 g_value_set_string (value, lightdm_user_get_background (self));
1521 case USER_PROP_LANGUAGE:
1522 g_value_set_string (value, lightdm_user_get_language (self));
1524 case USER_PROP_LAYOUT:
1525 g_value_set_string (value, lightdm_user_get_layout (self));
1527 case USER_PROP_LAYOUTS:
1528 g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
1530 case USER_PROP_SESSION:
1531 g_value_set_string (value, lightdm_user_get_session (self));
1533 case USER_PROP_LOGGED_IN:
1534 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1536 case USER_PROP_HAS_MESSAGES:
1537 g_value_set_boolean (value, lightdm_user_get_has_messages (self));
1540 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1546 lightdm_user_finalize (GObject *object)
1548 LightDMUser *self = LIGHTDM_USER (object);
1549 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1551 g_free (priv->name);
1552 g_free (priv->real_name);
1553 g_free (priv->home_directory);
1554 g_free (priv->image);
1555 g_free (priv->background);
1556 g_strfreev (priv->layouts);
1557 if (priv->dmrc_file)
1558 g_key_file_free (priv->dmrc_file);
1562 lightdm_user_class_init (LightDMUserClass *klass)
1564 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1566 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1568 object_class->set_property = lightdm_user_set_property;
1569 object_class->get_property = lightdm_user_get_property;
1570 object_class->finalize = lightdm_user_finalize;
1572 g_object_class_install_property (object_class,
1574 g_param_spec_string ("name",
1578 G_PARAM_READWRITE));
1579 g_object_class_install_property (object_class,
1580 USER_PROP_REAL_NAME,
1581 g_param_spec_string ("real-name",
1585 G_PARAM_READWRITE));
1586 g_object_class_install_property (object_class,
1587 USER_PROP_DISPLAY_NAME,
1588 g_param_spec_string ("display-name",
1590 "Users display name",
1593 g_object_class_install_property (object_class,
1594 USER_PROP_HOME_DIRECTORY,
1595 g_param_spec_string ("home-directory",
1599 G_PARAM_READWRITE));
1600 g_object_class_install_property (object_class,
1602 g_param_spec_string ("image",
1606 G_PARAM_READWRITE));
1607 g_object_class_install_property (object_class,
1608 USER_PROP_BACKGROUND,
1609 g_param_spec_string ("background",
1613 G_PARAM_READWRITE));
1614 g_object_class_install_property (object_class,
1616 g_param_spec_string ("language",
1618 "Language used by this user",
1621 g_object_class_install_property (object_class,
1623 g_param_spec_string ("layout",
1625 "Keyboard layout used by this user",
1628 g_object_class_install_property (object_class,
1630 g_param_spec_boxed ("layouts",
1632 "Keyboard layouts used by this user",
1635 g_object_class_install_property (object_class,
1637 g_param_spec_string ("session",
1639 "Session used by this user",
1642 g_object_class_install_property (object_class,
1643 USER_PROP_LOGGED_IN,
1644 g_param_spec_boolean ("logged-in",
1646 "TRUE if the user is currently in a session",
1648 G_PARAM_READWRITE));
1649 g_object_class_install_property (object_class,
1650 USER_PROP_LOGGED_IN,
1651 g_param_spec_boolean ("has-messages",
1653 "TRUE if the user is has waiting messages",
1655 G_PARAM_READWRITE));
1658 * LightDMUser::changed:
1659 * @user: A #LightDMUser
1661 * The ::changed signal gets emitted this user account is modified.
1663 user_signals[CHANGED] =
1664 g_signal_new ("changed",
1665 G_TYPE_FROM_CLASS (klass),
1667 G_STRUCT_OFFSET (LightDMUserClass, changed),
1674 session_init (Session *session)
1679 session_finalize (GObject *object)
1681 Session *self = SESSION (object);
1683 g_free (self->path);
1684 g_free (self->username);
1688 session_class_init (SessionClass *klass)
1690 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1691 object_class->finalize = session_finalize;