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,
44 USER_PROP_HAS_MESSAGES
54 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
61 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
65 /* Connection to AccountsService */
66 GDBusProxy *accounts_service_proxy;
67 GList *user_account_objects;
69 /* Connection to DisplayManager */
70 GDBusProxy *display_manager_proxy;
72 /* File monitor for password file */
73 GFileMonitor *passwd_monitor;
75 /* TRUE if have scanned users */
81 /* List of sessions */
83 } LightDMUserListPrivate;
93 LightDMUserList *user_list;
97 gchar *home_directory;
100 gboolean has_messages;
106 } LightDMUserPrivate;
110 GObject parent_instance;
117 GObjectClass parent_class;
120 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
121 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
122 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
123 GType session_get_type (void);
124 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
126 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
127 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
129 #define PASSWD_FILE "/etc/passwd"
130 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
132 static LightDMUserList *singleton = NULL;
135 * lightdm_user_list_get_instance:
139 * Return value: (transfer none): the #LightDMUserList
142 lightdm_user_list_get_instance (void)
145 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
150 get_user_by_name (LightDMUserList *user_list, const gchar *username)
152 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
155 for (link = priv->users; link; link = link->next)
157 LightDMUser *user = link->data;
158 if (strcmp (lightdm_user_get_name (user), username) == 0)
166 compare_user (gconstpointer a, gconstpointer b)
168 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
169 return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
173 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
175 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
177 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
178 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
179 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
182 g_free (priv->real_name);
183 priv->real_name = g_strdup (real_name);
184 g_free (priv->home_directory);
185 priv->home_directory = g_strdup (home_directory);
186 g_free (priv->image);
187 priv->image = g_strdup (image);
193 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
195 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
199 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
201 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
205 gchar **hidden_users, **hidden_shells;
206 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
207 GError *error = NULL;
209 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
211 config = g_key_file_new ();
212 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
213 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
214 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
215 g_clear_error (&error);
217 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
218 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
222 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
224 value = g_strdup ("nobody nobody4 noaccess");
225 hidden_users = g_strsplit (value, " ", -1);
228 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
230 value = g_strdup ("/bin/false /usr/sbin/nologin");
231 hidden_shells = g_strsplit (value, " ", -1);
234 g_key_file_free (config);
240 struct passwd *entry;
242 LightDMUserPrivate *user_priv;
244 gchar *real_name, *image;
252 /* Ignore system users */
253 if (entry->pw_uid < minimum_uid)
256 /* Ignore users disabled by shell */
259 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
260 if (hidden_shells[i])
264 /* Ignore certain users */
265 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
269 tokens = g_strsplit (entry->pw_gecos, ",", -1);
270 if (tokens[0] != NULL && tokens[0][0] != '\0')
271 real_name = g_strdup (tokens[0]);
273 real_name = g_strdup ("");
276 image = g_build_filename (entry->pw_dir, ".face", NULL);
277 if (!g_file_test (image, G_FILE_TEST_EXISTS))
280 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
281 if (!g_file_test (image, G_FILE_TEST_EXISTS))
288 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
289 user_priv = GET_USER_PRIVATE (user);
290 user_priv->user_list = user_list;
291 g_free (user_priv->name);
292 user_priv->name = g_strdup (entry->pw_name);
293 g_free (user_priv->real_name);
294 user_priv->real_name = real_name;
295 g_free (user_priv->home_directory);
296 user_priv->home_directory = g_strdup (entry->pw_dir);
297 g_free (user_priv->image);
298 user_priv->image = image;
300 /* Update existing users if have them */
301 for (link = priv->users; link; link = link->next)
303 LightDMUser *info = link->data;
304 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
306 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
307 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
308 g_object_unref (user);
315 /* Only notify once we have loaded the user list */
316 if (priv->have_users)
317 new_users = g_list_insert_sorted (new_users, user, compare_user);
319 users = g_list_insert_sorted (users, user, compare_user);
321 g_strfreev (hidden_users);
322 g_strfreev (hidden_shells);
325 g_warning ("Failed to read password database: %s", strerror (errno));
329 /* Use new user list */
330 old_users = priv->users;
333 /* Notify of changes */
334 for (link = new_users; link; link = link->next)
336 LightDMUser *info = link->data;
337 g_debug ("User %s added", lightdm_user_get_name (info));
338 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
340 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
342 g_list_free (new_users);
343 for (link = changed_users; link; link = link->next)
345 LightDMUser *info = link->data;
346 g_debug ("User %s changed", lightdm_user_get_name (info));
347 g_signal_emit (info, user_signals[CHANGED], 0);
349 g_list_free (changed_users);
350 for (link = old_users; link; link = link->next)
354 /* See if this user is in the current list */
355 for (new_link = priv->users; new_link; new_link = new_link->next)
357 if (new_link->data == link->data)
363 LightDMUser *info = link->data;
364 g_debug ("User %s removed", lightdm_user_get_name (info));
365 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
366 g_object_unref (info);
369 g_list_free (old_users);
373 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
375 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
377 g_debug ("%s changed, reloading user list", g_file_get_path (file));
378 load_passwd_file (user_list, TRUE);
383 update_user (UserAccountObject *object)
385 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
386 GVariant *result, *value;
389 GError *error = NULL;
391 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
392 "org.freedesktop.Accounts",
393 g_dbus_proxy_get_object_path (object->proxy),
394 "org.freedesktop.DBus.Properties",
396 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
397 G_VARIANT_TYPE ("(a{sv})"),
398 G_DBUS_CALL_FLAGS_NONE,
403 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
404 g_clear_error (&error);
408 g_variant_get (result, "(a{sv})", &iter);
409 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
411 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
414 g_variant_get (value, "&s", &user_name);
416 priv->name = g_strdup (user_name);
418 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
421 g_variant_get (value, "&s", &real_name);
422 g_free (priv->real_name);
423 priv->real_name = g_strdup (real_name);
425 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
427 gchar *home_directory;
428 g_variant_get (value, "&s", &home_directory);
429 g_free (priv->home_directory);
430 priv->home_directory = g_strdup (home_directory);
432 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
435 g_variant_get (value, "&s", &icon_file);
436 g_free (priv->image);
437 if (strcmp (icon_file, "") == 0)
440 priv->image = g_strdup (icon_file);
442 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
444 gchar *background_file;
445 g_variant_get (value, "&s", &background_file);
446 g_free (priv->background);
447 if (strcmp (background_file, "") == 0)
448 priv->background = NULL;
450 priv->background = g_strdup (background_file);
453 g_variant_iter_free (iter);
455 g_variant_unref (result);
461 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
463 if (strcmp (signal_name, "Changed") == 0)
465 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
467 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
468 update_user (object);
469 g_signal_emit (object->user, user_signals[CHANGED], 0);
472 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
476 static UserAccountObject *
477 user_account_object_new (LightDMUserList *user_list, const gchar *path)
480 UserAccountObject *object;
481 GError *error = NULL;
483 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
484 G_DBUS_PROXY_FLAGS_NONE,
486 "org.freedesktop.Accounts",
488 "org.freedesktop.Accounts.User",
492 g_warning ("Error getting user %s: %s", path, error->message);
493 g_clear_error (&error);
497 object = g_malloc0 (sizeof (UserAccountObject));
498 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
499 GET_USER_PRIVATE (object->user)->user_list = user_list;
500 object->proxy = proxy;
501 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
507 user_account_object_free (UserAccountObject *object)
511 g_object_unref (object->user);
512 g_object_unref (object->proxy);
516 static UserAccountObject *
517 find_user_account_object (LightDMUserList *user_list, const gchar *path)
519 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
522 for (link = priv->user_account_objects; link; link = link->next)
524 UserAccountObject *object = link->data;
525 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
533 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
535 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
537 if (strcmp (signal_name, "UserAdded") == 0)
539 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
542 UserAccountObject *object;
544 g_variant_get (parameters, "(&o)", &path);
546 /* Ignore duplicate requests */
547 object = find_user_account_object (user_list, path);
551 object = user_account_object_new (user_list, path);
552 if (object && update_user (object))
554 g_debug ("User %s added", path);
555 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
556 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
557 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
558 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
561 user_account_object_free (object);
564 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
566 else if (strcmp (signal_name, "UserDeleted") == 0)
568 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
571 UserAccountObject *object;
573 g_variant_get (parameters, "(&o)", &path);
575 object = find_user_account_object (user_list, path);
579 g_debug ("User %s deleted", path);
580 priv->users = g_list_remove (priv->users, object->user);
581 g_object_unref (object->user);
583 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
585 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
586 user_account_object_free (object);
589 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
594 load_session (LightDMUserList *user_list, const gchar *path)
596 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
597 Session *session = NULL;
598 GVariant *result, *username;
599 GError *error = NULL;
601 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
602 "org.freedesktop.DisplayManager",
604 "org.freedesktop.DBus.Properties",
606 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
607 G_VARIANT_TYPE ("(v)"),
608 G_DBUS_CALL_FLAGS_NONE,
613 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
614 g_clear_error (&error);
618 g_variant_get (result, "(v)", &username);
619 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
623 g_variant_get (username, "&s", &name);
625 g_debug ("Loaded session %s (%s)", path, name);
626 session = g_object_new (session_get_type (), NULL);
627 session->username = g_strdup (name);
628 session->path = g_strdup (path);
629 priv->sessions = g_list_append (priv->sessions, session);
631 g_variant_unref (username);
632 g_variant_unref (result);
638 display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
640 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
642 if (strcmp (signal_name, "SessionAdded") == 0)
644 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
648 LightDMUser *user = NULL;
650 g_variant_get (parameters, "(&o)", &path);
651 session = load_session (user_list, path);
653 user = get_user_by_name (user_list, session->username);
655 g_signal_emit (user, user_signals[CHANGED], 0);
658 else if (strcmp (signal_name, "SessionRemoved") == 0)
660 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
665 g_variant_get (parameters, "(&o)", &path);
667 for (link = priv->sessions; link; link = link->next)
669 Session *session = link->data;
670 if (strcmp (session->path, path) == 0)
674 g_debug ("Session %s removed", path);
675 priv->sessions = g_list_remove_link (priv->sessions, link);
676 user = get_user_by_name (user_list, session->username);
678 g_signal_emit (user, user_signals[CHANGED], 0);
679 g_object_unref (session);
688 update_users (LightDMUserList *user_list)
690 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
691 GError *error = NULL;
693 if (priv->have_users)
695 priv->have_users = TRUE;
697 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
698 G_DBUS_PROXY_FLAGS_NONE,
700 "org.freedesktop.Accounts",
701 "/org/freedesktop/Accounts",
702 "org.freedesktop.Accounts",
706 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
707 g_clear_error (&error);
709 /* Check if the service exists */
710 if (priv->accounts_service_proxy)
714 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
717 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
718 g_object_unref (priv->accounts_service_proxy);
719 priv->accounts_service_proxy = NULL;
724 if (priv->accounts_service_proxy)
728 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
730 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
732 g_variant_new ("()"),
733 G_DBUS_CALL_FLAGS_NONE,
738 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
739 g_clear_error (&error);
743 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
748 g_debug ("Loading users from org.freedesktop.Accounts");
749 g_variant_get (result, "(ao)", &iter);
750 while (g_variant_iter_loop (iter, "&o", &path))
752 UserAccountObject *object;
754 g_debug ("Loading user %s", path);
756 object = user_account_object_new (user_list, path);
757 if (object && update_user (object))
759 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
760 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
761 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
764 user_account_object_free (object);
766 g_variant_iter_free (iter);
769 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
771 g_variant_unref (result);
777 load_passwd_file (user_list, FALSE);
779 /* Watch for changes to user list */
781 passwd_file = g_file_new_for_path (PASSWD_FILE);
782 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
783 g_object_unref (passwd_file);
785 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
787 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
788 g_clear_error (&error);
791 priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
792 G_DBUS_PROXY_FLAGS_NONE,
794 "org.freedesktop.DisplayManager",
795 "/org/freedesktop/DisplayManager",
796 "org.freedesktop.DisplayManager",
800 g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
801 g_clear_error (&error);
803 if (priv->display_manager_proxy)
807 g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
809 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
810 "org.freedesktop.DisplayManager",
811 "/org/freedesktop/DisplayManager",
812 "org.freedesktop.DBus.Properties",
814 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
815 G_VARIANT_TYPE ("(v)"),
816 G_DBUS_CALL_FLAGS_NONE,
821 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
822 g_clear_error (&error);
826 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
832 g_variant_get (result, "(v)", &value);
834 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
835 g_variant_get (value, "ao", &iter);
836 while (g_variant_iter_loop (iter, "&o", &path))
837 load_session (user_list, path);
838 g_variant_iter_free (iter);
840 g_variant_unref (value);
843 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
845 g_variant_unref (result);
850 * lightdm_user_list_get_length:
851 * @user_list: a #LightDMUserList
853 * Return value: The number of users able to log in
856 lightdm_user_list_get_length (LightDMUserList *user_list)
858 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
859 update_users (user_list);
860 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
864 * lightdm_user_list_get_users:
865 * @user_list: A #LightDMUserList
867 * Get a list of users to present to the user. This list may be a subset of the
868 * available users and may be empty depending on the server configuration.
870 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
873 lightdm_user_list_get_users (LightDMUserList *user_list)
875 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
876 update_users (user_list);
877 return GET_LIST_PRIVATE (user_list)->users;
881 * lightdm_user_list_get_user_by_name:
882 * @user_list: A #LightDMUserList
883 * @username: Name of user to get.
885 * Get infomation about a given user or #NULL if this user doesn't exist.
887 * Return value: (transfer none): A #LightDMUser entry for the given user.
890 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
892 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
893 g_return_val_if_fail (username != NULL, NULL);
895 update_users (user_list);
897 return get_user_by_name (user_list, username);
901 lightdm_user_list_init (LightDMUserList *user_list)
906 lightdm_user_list_set_property (GObject *object,
911 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
915 lightdm_user_list_get_property (GObject *object,
920 LightDMUserList *self;
922 self = LIGHTDM_USER_LIST (object);
926 case LIST_PROP_NUM_USERS:
927 g_value_set_int (value, lightdm_user_list_get_length (self));
930 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
936 lightdm_user_list_finalize (GObject *object)
938 LightDMUserList *self = LIGHTDM_USER_LIST (object);
939 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
941 if (priv->accounts_service_proxy)
942 g_object_unref (priv->accounts_service_proxy);
943 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
944 if (priv->passwd_monitor)
945 g_object_unref (priv->passwd_monitor);
946 g_list_free_full (priv->users, g_object_unref);
947 g_list_free_full (priv->sessions, g_object_unref);
949 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
953 lightdm_user_list_class_init (LightDMUserListClass *klass)
955 GObjectClass *object_class = G_OBJECT_CLASS (klass);
957 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
959 object_class->set_property = lightdm_user_list_set_property;
960 object_class->get_property = lightdm_user_list_get_property;
961 object_class->finalize = lightdm_user_list_finalize;
963 g_object_class_install_property (object_class,
965 g_param_spec_int ("num-users",
967 "Number of login users",
971 * LightDMUserList::user-added:
972 * @user_list: A #LightDMUserList
973 * @user: The #LightDM user that has been added.
975 * The ::user-added signal gets emitted when a user account is created.
977 list_signals[USER_ADDED] =
978 g_signal_new ("user-added",
979 G_TYPE_FROM_CLASS (klass),
981 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
983 g_cclosure_marshal_VOID__OBJECT,
984 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
987 * LightDMUserList::user-changed:
988 * @user_list: A #LightDMUserList
989 * @user: The #LightDM user that has been changed.
991 * The ::user-changed signal gets emitted when a user account is modified.
993 list_signals[USER_CHANGED] =
994 g_signal_new ("user-changed",
995 G_TYPE_FROM_CLASS (klass),
997 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
999 g_cclosure_marshal_VOID__OBJECT,
1000 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1003 * LightDMUserList::user-removed:
1004 * @user_list: A #LightDMUserList
1005 * @user: The #LightDM user that has been removed.
1007 * The ::user-removed signal gets emitted when a user account is removed.
1009 list_signals[USER_REMOVED] =
1010 g_signal_new ("user-removed",
1011 G_TYPE_FROM_CLASS (klass),
1013 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1015 g_cclosure_marshal_VOID__OBJECT,
1016 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1020 * lightdm_user_get_name:
1021 * @user: A #LightDMUser
1023 * Get the name of a user.
1025 * Return value: The name of the given user
1028 lightdm_user_get_name (LightDMUser *user)
1030 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1031 return GET_USER_PRIVATE (user)->name;
1035 * lightdm_user_get_real_name:
1036 * @user: A #LightDMUser
1038 * Get the real name of a user.
1040 * Return value: The real name of the given user
1043 lightdm_user_get_real_name (LightDMUser *user)
1045 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1046 return GET_USER_PRIVATE (user)->real_name;
1050 * lightdm_user_get_display_name:
1051 * @user: A #LightDMUser
1053 * Get the display name of a user.
1055 * Return value: The display name of the given user
1058 lightdm_user_get_display_name (LightDMUser *user)
1060 LightDMUserPrivate *priv;
1062 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1064 priv = GET_USER_PRIVATE (user);
1065 if (strcmp (priv->real_name, ""))
1066 return priv->real_name;
1072 * lightdm_user_get_home_directory:
1073 * @user: A #LightDMUser
1075 * Get the home directory for a user.
1077 * Return value: The users home directory
1080 lightdm_user_get_home_directory (LightDMUser *user)
1082 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1083 return GET_USER_PRIVATE (user)->home_directory;
1087 * lightdm_user_get_image:
1088 * @user: A #LightDMUser
1090 * Get the image URI for a user.
1092 * Return value: The image URI for the given user or #NULL if no URI
1095 lightdm_user_get_image (LightDMUser *user)
1097 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1098 return GET_USER_PRIVATE (user)->image;
1102 * lightdm_user_get_background:
1103 * @user: A #LightDMUser
1105 * Get the background file path for a user.
1107 * Return value: The background file path for the given user or #NULL if no path
1110 lightdm_user_get_background (LightDMUser *user)
1112 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1113 return GET_USER_PRIVATE (user)->background;
1117 load_dmrc (LightDMUser *user)
1119 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1121 //gboolean have_dmrc;
1123 if (!priv->dmrc_file)
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, '.');
1149 g_strfreev (priv->layouts);
1150 priv->layouts = NULL;
1152 if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1154 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1155 priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1156 priv->layouts[1] = NULL;
1160 g_free (priv->session);
1161 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1165 get_property (GDBusProxy *proxy, const gchar *property)
1172 answer = g_dbus_proxy_get_cached_property (proxy, property);
1176 g_warning ("Could not get accounts property %s", property);
1184 get_boolean_property (GDBusProxy *proxy, const gchar *property)
1189 answer = get_property (proxy, property);
1190 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN))
1192 g_warning ("Unexpected accounts property type for %s: %s",
1193 property, g_variant_get_type_string (answer));
1194 g_variant_unref (answer);
1198 rv = g_variant_get_boolean (answer);
1199 g_variant_unref (answer);
1205 get_string_property (GDBusProxy *proxy, const gchar *property)
1210 answer = get_property (proxy, property);
1211 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING))
1213 g_warning ("Unexpected accounts property type for %s: %s",
1214 property, g_variant_get_type_string (answer));
1215 g_variant_unref (answer);
1219 rv = g_strdup (g_variant_get_string (answer, NULL));
1220 if (strcmp (rv, "") == 0)
1225 g_variant_unref (answer);
1231 get_string_array_property (GDBusProxy *proxy, const gchar *property)
1239 answer = g_dbus_proxy_get_cached_property (proxy, property);
1243 g_warning ("Could not get accounts property %s", property);
1247 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
1249 g_warning ("Unexpected accounts property type for %s: %s",
1250 property, g_variant_get_type_string (answer));
1251 g_variant_unref (answer);
1255 rv = g_variant_dup_strv (answer, NULL);
1257 g_variant_unref (answer);
1262 load_accounts_service (LightDMUser *user)
1264 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1265 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1266 UserAccountObject *account = NULL;
1270 /* First, find AccountObject proxy */
1271 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1273 UserAccountObject *a = iter->data;
1274 if (a->user == user)
1283 /* We have proxy, let's grab some properties */
1285 g_free (priv->language);
1286 priv->language = get_string_property (account->proxy, "Language");
1288 g_free (priv->session);
1289 priv->session = get_string_property (account->proxy, "XSession");
1291 value = get_string_array_property (account->proxy, "XKeyboardLayouts");
1296 g_strfreev (priv->layouts);
1297 priv->layouts = value;
1303 priv->has_messages = get_boolean_property (account->proxy, "XHasMessages");
1308 /* Loads language/layout/session info for user */
1310 load_user_values (LightDMUser *user)
1312 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1315 load_accounts_service (user); // overrides dmrc values
1317 /* Ensure a few guarantees */
1318 if (priv->layouts == NULL)
1320 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1321 priv->layouts[0] = NULL;
1326 * lightdm_user_get_language:
1327 * @user: A #LightDMUser
1329 * Get the language for a user.
1331 * Return value: The language for the given user or #NULL if using system defaults.
1334 lightdm_user_get_language (LightDMUser *user)
1336 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1337 load_user_values (user);
1338 return GET_USER_PRIVATE (user)->language;
1342 * lightdm_user_get_layout:
1343 * @user: A #LightDMUser
1345 * Get the keyboard layout for a user.
1347 * 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.
1350 lightdm_user_get_layout (LightDMUser *user)
1352 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1353 load_user_values (user);
1354 return GET_USER_PRIVATE (user)->layouts[0];
1358 * lightdm_user_get_layouts:
1359 * @user: A #LightDMUser
1361 * Get the configured keyboard layouts for a user.
1363 * 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.
1365 const gchar * const *
1366 lightdm_user_get_layouts (LightDMUser *user)
1368 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1369 load_user_values (user);
1370 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1374 * lightdm_user_get_session:
1375 * @user: A #LightDMUser
1377 * Get the session for a user.
1379 * Return value: The session for the given user or #NULL if using system defaults.
1382 lightdm_user_get_session (LightDMUser *user)
1384 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1385 load_user_values (user);
1386 return GET_USER_PRIVATE (user)->session;
1390 * lightdm_user_get_logged_in:
1391 * @user: A #LightDMUser
1393 * Check if a user is logged in.
1395 * Return value: #TRUE if the user is currently logged in.
1398 lightdm_user_get_logged_in (LightDMUser *user)
1400 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1401 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1404 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1406 for (link = list_priv->sessions; link; link = link->next)
1408 Session *session = link->data;
1409 if (strcmp (session->username, priv->name) == 0)
1417 * lightdm_user_get_has_messages:
1418 * @user: A #LightDMUser
1420 * Check if a user has waiting messages.
1422 * Return value: #TRUE if the user has waiting messages.
1425 lightdm_user_get_has_messages (LightDMUser *user)
1427 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1428 load_user_values (user);
1429 return GET_USER_PRIVATE (user)->has_messages;
1433 lightdm_user_init (LightDMUser *user)
1438 lightdm_user_set_property (GObject *object,
1440 const GValue *value,
1443 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1447 lightdm_user_get_property (GObject *object,
1454 self = LIGHTDM_USER (object);
1458 case USER_PROP_NAME:
1459 g_value_set_string (value, lightdm_user_get_name (self));
1461 case USER_PROP_REAL_NAME:
1462 g_value_set_string (value, lightdm_user_get_real_name (self));
1464 case USER_PROP_DISPLAY_NAME:
1465 g_value_set_string (value, lightdm_user_get_display_name (self));
1467 case USER_PROP_HOME_DIRECTORY:
1468 g_value_set_string (value, lightdm_user_get_home_directory (self));
1470 case USER_PROP_IMAGE:
1471 g_value_set_string (value, lightdm_user_get_image (self));
1473 case USER_PROP_BACKGROUND:
1474 g_value_set_string (value, lightdm_user_get_background (self));
1476 case USER_PROP_LANGUAGE:
1477 g_value_set_string (value, lightdm_user_get_language (self));
1479 case USER_PROP_LAYOUT:
1480 g_value_set_string (value, lightdm_user_get_layout (self));
1482 case USER_PROP_LAYOUTS:
1483 g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
1485 case USER_PROP_SESSION:
1486 g_value_set_string (value, lightdm_user_get_session (self));
1488 case USER_PROP_LOGGED_IN:
1489 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1491 case USER_PROP_HAS_MESSAGES:
1492 g_value_set_boolean (value, lightdm_user_get_has_messages (self));
1495 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1501 lightdm_user_finalize (GObject *object)
1503 LightDMUser *self = LIGHTDM_USER (object);
1504 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1506 g_free (priv->name);
1507 g_free (priv->real_name);
1508 g_free (priv->home_directory);
1509 g_free (priv->image);
1510 g_free (priv->background);
1511 g_strfreev (priv->layouts);
1512 if (priv->dmrc_file)
1513 g_key_file_free (priv->dmrc_file);
1517 lightdm_user_class_init (LightDMUserClass *klass)
1519 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1521 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1523 object_class->set_property = lightdm_user_set_property;
1524 object_class->get_property = lightdm_user_get_property;
1525 object_class->finalize = lightdm_user_finalize;
1527 g_object_class_install_property (object_class,
1529 g_param_spec_string ("name",
1533 G_PARAM_READWRITE));
1534 g_object_class_install_property (object_class,
1535 USER_PROP_REAL_NAME,
1536 g_param_spec_string ("real-name",
1540 G_PARAM_READWRITE));
1541 g_object_class_install_property (object_class,
1542 USER_PROP_DISPLAY_NAME,
1543 g_param_spec_string ("display-name",
1545 "Users display name",
1548 g_object_class_install_property (object_class,
1549 USER_PROP_HOME_DIRECTORY,
1550 g_param_spec_string ("home-directory",
1554 G_PARAM_READWRITE));
1555 g_object_class_install_property (object_class,
1557 g_param_spec_string ("image",
1561 G_PARAM_READWRITE));
1562 g_object_class_install_property (object_class,
1563 USER_PROP_BACKGROUND,
1564 g_param_spec_string ("background",
1568 G_PARAM_READWRITE));
1569 g_object_class_install_property (object_class,
1571 g_param_spec_string ("language",
1573 "Language used by this user",
1576 g_object_class_install_property (object_class,
1578 g_param_spec_string ("layout",
1580 "Keyboard layout used by this user",
1583 g_object_class_install_property (object_class,
1585 g_param_spec_boxed ("layouts",
1587 "Keyboard layouts used by this user",
1590 g_object_class_install_property (object_class,
1592 g_param_spec_string ("session",
1594 "Session used by this user",
1597 g_object_class_install_property (object_class,
1598 USER_PROP_LOGGED_IN,
1599 g_param_spec_boolean ("logged-in",
1601 "TRUE if the user is currently in a session",
1603 G_PARAM_READWRITE));
1604 g_object_class_install_property (object_class,
1605 USER_PROP_LOGGED_IN,
1606 g_param_spec_boolean ("has-messages",
1608 "TRUE if the user is has waiting messages",
1610 G_PARAM_READWRITE));
1613 * LightDMUser::changed:
1614 * @user: A #LightDMUser
1616 * The ::changed signal gets emitted this user account is modified.
1618 user_signals[CHANGED] =
1619 g_signal_new ("changed",
1620 G_TYPE_FROM_CLASS (klass),
1622 G_STRUCT_OFFSET (LightDMUserClass, changed),
1624 g_cclosure_marshal_VOID__VOID,
1629 session_init (Session *session)
1634 session_finalize (GObject *object)
1636 Session *self = SESSION (object);
1638 g_free (self->path);
1639 g_free (self->username);
1643 session_class_init (SessionClass *klass)
1645 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1646 object_class->finalize = session_finalize;