2 * Copyright (C) 2010 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; either version 3 of the License, or (at your option) any
8 * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
16 #include <sys/utsname.h>
20 #include "lightdm/user.h"
34 USER_PROP_DISPLAY_NAME,
35 USER_PROP_HOME_DIRECTORY,
50 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
57 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
61 /* Connection to AccountsService */
62 GDBusProxy *accounts_service_proxy;
63 GList *user_account_objects;
65 /* Connection to DisplayManager */
66 GDBusProxy *display_manager_proxy;
68 /* File monitor for password file */
69 GFileMonitor *passwd_monitor;
71 /* TRUE if have scanned users */
77 /* List of sessions */
79 } LightDMUserListPrivate;
89 LightDMUserList *user_list;
93 gchar *home_directory;
100 } LightDMUserPrivate;
104 GObject parent_instance;
111 GObjectClass parent_class;
114 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
115 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
116 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
117 GType session_get_type (void);
118 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
120 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
121 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
123 #define PASSWD_FILE "/etc/passwd"
124 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
126 static LightDMUserList *singleton = NULL;
129 * lightdm_user_list_get_instance:
133 * Return value: (tranfer none): the #LightDMUserList
136 lightdm_user_list_get_instance (void)
139 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
144 get_user_by_name (LightDMUserList *user_list, const gchar *username)
146 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
149 for (link = priv->users; link; link = link->next)
151 LightDMUser *user = link->data;
152 if (strcmp (lightdm_user_get_name (user), username) == 0)
160 compare_user (gconstpointer a, gconstpointer b)
162 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
163 return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
167 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
169 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
171 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
172 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
173 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
176 g_free (priv->real_name);
177 priv->real_name = g_strdup (real_name);
178 g_free (priv->home_directory);
179 priv->home_directory = g_strdup (home_directory);
180 g_free (priv->image);
181 priv->image = g_strdup (image);
187 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
189 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
193 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
195 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
199 gchar **hidden_users, **hidden_shells;
200 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
201 GError *error = NULL;
203 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
205 config = g_key_file_new ();
206 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
207 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
208 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
209 g_clear_error (&error);
211 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
212 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
216 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
218 value = g_strdup ("nobody nobody4 noaccess");
219 hidden_users = g_strsplit (value, " ", -1);
222 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
224 value = g_strdup ("/bin/false /usr/sbin/nologin");
225 hidden_shells = g_strsplit (value, " ", -1);
228 g_key_file_free (config);
234 struct passwd *entry;
236 LightDMUserPrivate *user_priv;
238 gchar *real_name, *image;
246 /* Ignore system users */
247 if (entry->pw_uid < minimum_uid)
250 /* Ignore users disabled by shell */
253 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
254 if (hidden_shells[i])
258 /* Ignore certain users */
259 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
263 tokens = g_strsplit (entry->pw_gecos, ",", -1);
264 if (tokens[0] != NULL && tokens[0][0] != '\0')
265 real_name = g_strdup (tokens[0]);
267 real_name = g_strdup ("");
270 image = g_build_filename (entry->pw_dir, ".face", NULL);
271 if (!g_file_test (image, G_FILE_TEST_EXISTS))
274 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
275 if (!g_file_test (image, G_FILE_TEST_EXISTS))
282 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
283 user_priv = GET_USER_PRIVATE (user);
284 user_priv->user_list = user_list;
285 g_free (user_priv->name);
286 user_priv->name = g_strdup (entry->pw_name);
287 g_free (user_priv->real_name);
288 user_priv->real_name = real_name;
289 g_free (user_priv->home_directory);
290 user_priv->home_directory = g_strdup (entry->pw_dir);
291 g_free (user_priv->image);
292 user_priv->image = image;
294 /* Update existing users if have them */
295 for (link = priv->users; link; link = link->next)
297 LightDMUser *info = link->data;
298 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
300 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
301 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
302 g_object_unref (user);
309 /* Only notify once we have loaded the user list */
310 if (priv->have_users)
311 new_users = g_list_insert_sorted (new_users, user, compare_user);
313 users = g_list_insert_sorted (users, user, compare_user);
315 g_strfreev (hidden_users);
316 g_strfreev (hidden_shells);
319 g_warning ("Failed to read password database: %s", strerror (errno));
323 /* Use new user list */
324 old_users = priv->users;
327 /* Notify of changes */
328 for (link = new_users; link; link = link->next)
330 LightDMUser *info = link->data;
331 g_debug ("User %s added", lightdm_user_get_name (info));
332 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
334 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
336 g_list_free (new_users);
337 for (link = changed_users; link; link = link->next)
339 LightDMUser *info = link->data;
340 g_debug ("User %s changed", lightdm_user_get_name (info));
341 g_signal_emit (info, user_signals[CHANGED], 0);
343 g_list_free (changed_users);
344 for (link = old_users; link; link = link->next)
348 /* See if this user is in the current list */
349 for (new_link = priv->users; new_link; new_link = new_link->next)
351 if (new_link->data == link->data)
357 LightDMUser *info = link->data;
358 g_debug ("User %s removed", lightdm_user_get_name (info));
359 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
360 g_object_unref (info);
363 g_list_free (old_users);
367 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
369 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
371 g_debug ("%s changed, reloading user list", g_file_get_path (file));
372 load_passwd_file (user_list, TRUE);
377 update_user (UserAccountObject *object)
379 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
380 GVariant *result, *value;
383 GError *error = NULL;
385 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
386 "org.freedesktop.Accounts",
387 g_dbus_proxy_get_object_path (object->proxy),
388 "org.freedesktop.DBus.Properties",
390 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
391 G_VARIANT_TYPE ("(a{sv})"),
392 G_DBUS_CALL_FLAGS_NONE,
397 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
398 g_clear_error (&error);
402 g_variant_get (result, "(a{sv})", &iter);
403 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
405 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
408 g_variant_get (value, "&s", &user_name);
410 priv->name = g_strdup (user_name);
412 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
415 g_variant_get (value, "&s", &real_name);
416 g_free (priv->real_name);
417 priv->real_name = g_strdup (real_name);
419 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
421 gchar *home_directory;
422 g_variant_get (value, "&s", &home_directory);
423 g_free (priv->home_directory);
424 priv->home_directory = g_strdup (home_directory);
426 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
429 g_variant_get (value, "&s", &icon_file);
430 g_free (priv->image);
431 if (strcmp (icon_file, "") == 0)
434 priv->image = g_strdup (icon_file);
437 g_variant_iter_free (iter);
439 g_variant_unref (result);
445 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
447 if (strcmp (signal_name, "Changed") == 0)
449 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
451 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
452 update_user (object);
453 g_signal_emit (object->user, user_signals[CHANGED], 0);
456 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
460 static UserAccountObject *
461 user_account_object_new (LightDMUserList *user_list, const gchar *path)
464 UserAccountObject *object;
465 GError *error = NULL;
467 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
468 G_DBUS_PROXY_FLAGS_NONE,
470 "org.freedesktop.Accounts",
472 "org.freedesktop.Accounts.User",
476 g_warning ("Error getting user %s: %s", path, error->message);
477 g_clear_error (&error);
481 object = g_malloc0 (sizeof (UserAccountObject));
482 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
483 GET_USER_PRIVATE (object->user)->user_list = user_list;
484 object->proxy = proxy;
485 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
491 user_account_object_free (UserAccountObject *object)
495 g_object_unref (object->user);
496 g_object_unref (object->proxy);
500 static UserAccountObject *
501 find_user_account_object (LightDMUserList *user_list, const gchar *path)
503 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
506 for (link = priv->user_account_objects; link; link = link->next)
508 UserAccountObject *object = link->data;
509 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
517 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
519 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
521 if (strcmp (signal_name, "UserAdded") == 0)
523 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
526 UserAccountObject *object;
528 g_variant_get (parameters, "(&o)", &path);
530 /* Ignore duplicate requests */
531 object = find_user_account_object (user_list, path);
535 object = user_account_object_new (user_list, path);
536 if (object && update_user (object))
538 g_debug ("User %s added", path);
539 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
540 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
541 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
542 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
545 user_account_object_free (object);
548 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
550 else if (strcmp (signal_name, "UserDeleted") == 0)
552 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
555 UserAccountObject *object;
557 g_variant_get (parameters, "(&o)", &path);
559 object = find_user_account_object (user_list, path);
563 g_debug ("User %s deleted", path);
564 priv->users = g_list_remove (priv->users, object->user);
565 g_object_unref (object->user);
567 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
569 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
570 user_account_object_free (object);
573 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
578 load_session (LightDMUserList *user_list, const gchar *path)
580 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
581 Session *session = NULL;
582 GVariant *result, *username;
583 GError *error = NULL;
585 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
586 "org.freedesktop.DisplayManager",
588 "org.freedesktop.DBus.Properties",
590 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
591 G_VARIANT_TYPE ("(v)"),
592 G_DBUS_CALL_FLAGS_NONE,
597 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
598 g_clear_error (&error);
602 g_variant_get (result, "(v)", &username);
603 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
607 g_variant_get (username, "&s", &name);
609 g_debug ("Loaded session %s (%s)", path, name);
610 session = g_object_new (session_get_type (), NULL);
611 session->username = g_strdup (name);
612 session->path = g_strdup (path);
613 priv->sessions = g_list_append (priv->sessions, session);
615 g_variant_unref (username);
616 g_variant_unref (result);
622 display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
624 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
626 if (strcmp (signal_name, "SessionAdded") == 0)
628 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
632 LightDMUser *user = NULL;
634 g_variant_get (parameters, "(&o)", &path);
635 session = load_session (user_list, path);
637 user = get_user_by_name (user_list, session->username);
639 g_signal_emit (user, user_signals[CHANGED], 0);
642 else if (strcmp (signal_name, "SessionRemoved") == 0)
644 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
649 g_variant_get (parameters, "(&o)", &path);
651 for (link = priv->sessions; link; link = link->next)
653 Session *session = link->data;
654 if (strcmp (session->path, path) == 0)
658 g_debug ("Session %s removed", path);
659 priv->sessions = g_list_remove_link (priv->sessions, link);
660 user = get_user_by_name (user_list, session->username);
662 g_signal_emit (user, user_signals[CHANGED], 0);
663 g_object_unref (session);
672 update_users (LightDMUserList *user_list)
674 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
675 GError *error = NULL;
677 if (priv->have_users)
679 priv->have_users = TRUE;
681 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
682 G_DBUS_PROXY_FLAGS_NONE,
684 "org.freedesktop.Accounts",
685 "/org/freedesktop/Accounts",
686 "org.freedesktop.Accounts",
690 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
691 g_clear_error (&error);
693 /* Check if the service exists */
694 if (priv->accounts_service_proxy)
698 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
701 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
702 g_object_unref (priv->accounts_service_proxy);
703 priv->accounts_service_proxy = NULL;
708 if (priv->accounts_service_proxy)
712 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
714 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
716 g_variant_new ("()"),
717 G_DBUS_CALL_FLAGS_NONE,
722 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
723 g_clear_error (&error);
727 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
732 g_debug ("Loading users from org.freedesktop.Accounts");
733 g_variant_get (result, "(ao)", &iter);
734 while (g_variant_iter_loop (iter, "&o", &path))
736 UserAccountObject *object;
738 g_debug ("Loading user %s", path);
740 object = user_account_object_new (user_list, path);
741 if (object && update_user (object))
743 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
744 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
745 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
748 user_account_object_free (object);
750 g_variant_iter_free (iter);
753 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
755 g_variant_unref (result);
761 load_passwd_file (user_list, FALSE);
763 /* Watch for changes to user list */
764 passwd_file = g_file_new_for_path (PASSWD_FILE);
765 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
766 g_object_unref (passwd_file);
768 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
770 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
771 g_clear_error (&error);
774 priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
775 G_DBUS_PROXY_FLAGS_NONE,
777 "org.freedesktop.DisplayManager",
778 "/org/freedesktop/DisplayManager",
779 "org.freedesktop.DisplayManager",
783 g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
784 g_clear_error (&error);
786 if (priv->display_manager_proxy)
790 g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
792 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
793 "org.freedesktop.DisplayManager",
794 "/org/freedesktop/DisplayManager",
795 "org.freedesktop.DBus.Properties",
797 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
798 G_VARIANT_TYPE ("(v)"),
799 G_DBUS_CALL_FLAGS_NONE,
804 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
805 g_clear_error (&error);
809 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
815 g_variant_get (result, "(v)", &value);
817 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
818 g_variant_get (value, "ao", &iter);
819 while (g_variant_iter_loop (iter, "&o", &path))
820 load_session (user_list, path);
821 g_variant_iter_free (iter);
823 g_variant_unref (value);
826 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
828 g_variant_unref (result);
833 * lightdm_user_list_get_length:
834 * @user_list: a #LightDMUserList
836 * Return value: The number of users able to log in
839 lightdm_user_list_get_length (LightDMUserList *user_list)
841 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
842 update_users (user_list);
843 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
847 * lightdm_user_list_get_users:
848 * @user_list: A #LightDMUserList
850 * Get a list of users to present to the user. This list may be a subset of the
851 * available users and may be empty depending on the server configuration.
853 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
856 lightdm_user_list_get_users (LightDMUserList *user_list)
858 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
859 update_users (user_list);
860 return GET_LIST_PRIVATE (user_list)->users;
864 * lightdm_user_list_get_user_by_name:
865 * @user_list: A #LightDMUserList
866 * @username: Name of user to get.
868 * Get infomation about a given user or #NULL if this user doesn't exist.
870 * Return value: (transfer none): A #LightDMUser entry for the given user.
873 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
875 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
876 g_return_val_if_fail (username != NULL, NULL);
878 update_users (user_list);
880 return get_user_by_name (user_list, username);
884 lightdm_user_list_init (LightDMUserList *user_list)
889 lightdm_user_list_set_property (GObject *object,
894 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
898 lightdm_user_list_get_property (GObject *object,
903 LightDMUserList *self;
905 self = LIGHTDM_USER_LIST (object);
909 case LIST_PROP_NUM_USERS:
910 g_value_set_int (value, lightdm_user_list_get_length (self));
913 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
919 lightdm_user_list_finalize (GObject *object)
921 LightDMUserList *self = LIGHTDM_USER_LIST (object);
922 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
924 if (priv->accounts_service_proxy)
925 g_object_unref (priv->accounts_service_proxy);
926 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
927 if (priv->passwd_monitor)
928 g_object_unref (priv->passwd_monitor);
929 g_list_free_full (priv->users, g_object_unref);
930 g_list_free_full (priv->sessions, g_object_unref);
932 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
936 lightdm_user_list_class_init (LightDMUserListClass *klass)
938 GObjectClass *object_class = G_OBJECT_CLASS (klass);
940 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
942 object_class->set_property = lightdm_user_list_set_property;
943 object_class->get_property = lightdm_user_list_get_property;
944 object_class->finalize = lightdm_user_list_finalize;
946 g_object_class_install_property (object_class,
948 g_param_spec_int ("num-users",
950 "Number of login users",
954 * LightDMUserList::user-added:
955 * @user_list: A #LightDMUserList
956 * @user: The #LightDM user that has been added.
958 * The ::user-added signal gets emitted when a user account is created.
960 list_signals[USER_ADDED] =
961 g_signal_new ("user-added",
962 G_TYPE_FROM_CLASS (klass),
964 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
966 g_cclosure_marshal_VOID__OBJECT,
967 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
970 * LightDMUserList::user-changed:
971 * @user_list: A #LightDMUserList
972 * @user: The #LightDM user that has been changed.
974 * The ::user-changed signal gets emitted when a user account is modified.
976 list_signals[USER_CHANGED] =
977 g_signal_new ("user-changed",
978 G_TYPE_FROM_CLASS (klass),
980 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
982 g_cclosure_marshal_VOID__OBJECT,
983 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
986 * LightDMUserList::user-removed:
987 * @user_list: A #LightDMUserList
988 * @user: The #LightDM user that has been removed.
990 * The ::user-removed signal gets emitted when a user account is removed.
992 list_signals[USER_REMOVED] =
993 g_signal_new ("user-removed",
994 G_TYPE_FROM_CLASS (klass),
996 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
998 g_cclosure_marshal_VOID__OBJECT,
999 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1003 * lightdm_user_get_name:
1004 * @user: A #LightDMUser
1006 * Get the name of a user.
1008 * Return value: The name of the given user
1011 lightdm_user_get_name (LightDMUser *user)
1013 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1014 return GET_USER_PRIVATE (user)->name;
1018 * lightdm_user_get_real_name:
1019 * @user: A #LightDMUser
1021 * Get the real name of a user.
1023 * Return value: The real name of the given user
1026 lightdm_user_get_real_name (LightDMUser *user)
1028 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1029 return GET_USER_PRIVATE (user)->real_name;
1033 * lightdm_user_get_display_name:
1034 * @user: A #LightDMUser
1036 * Get the display name of a user.
1038 * Return value: The display name of the given user
1041 lightdm_user_get_display_name (LightDMUser *user)
1043 LightDMUserPrivate *priv;
1045 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1047 priv = GET_USER_PRIVATE (user);
1048 if (strcmp (priv->real_name, ""))
1049 return priv->real_name;
1055 * lightdm_user_get_home_directory:
1056 * @user: A #LightDMUser
1058 * Get the home directory for a user.
1060 * Return value: The users home directory
1063 lightdm_user_get_home_directory (LightDMUser *user)
1065 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1066 return GET_USER_PRIVATE (user)->home_directory;
1070 * lightdm_user_get_image:
1071 * @user: A #LightDMUser
1073 * Get the image URI for a user.
1075 * Return value: The image URI for the given user or #NULL if no URI
1078 lightdm_user_get_image (LightDMUser *user)
1080 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1081 return GET_USER_PRIVATE (user)->image;
1085 load_dmrc (LightDMUser *user)
1087 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1089 //gboolean have_dmrc;
1091 priv->dmrc_file = g_key_file_new ();
1093 /* Load from the user directory */
1094 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1095 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1098 /* If no ~/.dmrc, then load from the cache */
1101 // FIXME: Watch for changes
1104 g_free (priv->language);
1106 g_free (priv->layout);
1108 g_free (priv->session);
1110 /* The Language field is actually a locale, strip the codeset off it to get the language */
1111 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1114 gchar *codeset = strchr (priv->language, '.');
1119 priv->layout = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1120 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1124 get_string_property (GDBusProxy *proxy, const gchar *property)
1132 answer = g_dbus_proxy_get_cached_property (proxy, property);
1136 g_warning ("Could not get accounts property %s", property);
1140 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("s")))
1142 g_warning ("Unexpected accounts property type for %s: %s",
1143 property, g_variant_get_type_string (answer));
1144 g_variant_unref (answer);
1148 g_variant_get (answer, "s", &rv);
1150 g_variant_unref (answer);
1155 load_accounts_service (LightDMUser *user)
1157 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1158 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1160 /* First, find AccountObject proxy */
1161 UserAccountObject *account = NULL;
1163 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1165 if (((UserAccountObject *)iter->data)->user == user)
1167 account = (UserAccountObject *)iter->data;
1174 /* We have proxy, let's grab some properties */
1176 g_free (priv->language);
1178 g_free (priv->session);
1179 priv->language = get_string_property (account->proxy, "Language");
1180 priv->session = get_string_property (account->proxy, "XSession");
1185 /* Loads language/layout/session info for user */
1187 load_user_values (LightDMUser *user)
1190 load_accounts_service (user); // overrides dmrc values
1194 * lightdm_user_get_language
1195 * @user: A #LightDMUser
1197 * Get the language for a user.
1199 * Return value: The language for the given user or #NULL if using system defaults.
1202 lightdm_user_get_language (LightDMUser *user)
1204 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1205 load_user_values (user);
1206 return GET_USER_PRIVATE (user)->language;
1210 * lightdm_user_get_layout
1211 * @user: A #LightDMUser
1213 * Get the keyboard layout for a user.
1215 * Return value: The keyboard layoyt for the given user or #NULL if using system defaults.
1218 lightdm_user_get_layout (LightDMUser *user)
1220 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1221 load_user_values (user);
1222 return GET_USER_PRIVATE (user)->layout;
1226 * lightdm_user_get_session
1227 * @user: A #LightDMUser
1229 * Get the session for a user.
1231 * Return value: The session for the given user or #NULL if using system defaults.
1234 lightdm_user_get_session (LightDMUser *user)
1236 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1237 load_user_values (user);
1238 return GET_USER_PRIVATE (user)->session;
1242 * lightdm_user_get_logged_in:
1243 * @user: A #LightDMUser
1245 * Check if a user is logged in.
1247 * Return value: #TRUE if the user is currently logged in.
1250 lightdm_user_get_logged_in (LightDMUser *user)
1252 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1253 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1256 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1258 for (link = list_priv->sessions; link; link = link->next)
1260 Session *session = link->data;
1261 if (strcmp (session->username, priv->name) == 0)
1269 lightdm_user_init (LightDMUser *user)
1271 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1273 priv->name = g_strdup ("");
1274 priv->real_name = g_strdup ("");
1275 priv->home_directory = g_strdup ("");
1276 priv->image = g_strdup ("");
1277 priv->language = g_strdup ("");
1278 priv->layout = g_strdup ("");
1279 priv->session = g_strdup ("");
1283 lightdm_user_set_property (GObject *object,
1285 const GValue *value,
1288 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1292 lightdm_user_get_property (GObject *object,
1299 self = LIGHTDM_USER (object);
1303 case USER_PROP_NAME:
1304 g_value_set_string (value, lightdm_user_get_name (self));
1306 case USER_PROP_REAL_NAME:
1307 g_value_set_string (value, lightdm_user_get_real_name (self));
1309 case USER_PROP_DISPLAY_NAME:
1310 g_value_set_string (value, lightdm_user_get_display_name (self));
1312 case USER_PROP_HOME_DIRECTORY:
1313 g_value_set_string (value, lightdm_user_get_home_directory (self));
1315 case USER_PROP_IMAGE:
1316 g_value_set_string (value, lightdm_user_get_image (self));
1318 case USER_PROP_LANGUAGE:
1319 g_value_set_string (value, lightdm_user_get_language (self));
1321 case USER_PROP_LAYOUT:
1322 g_value_set_string (value, lightdm_user_get_layout (self));
1324 case USER_PROP_SESSION:
1325 g_value_set_string (value, lightdm_user_get_session (self));
1327 case USER_PROP_LOGGED_IN:
1328 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1337 lightdm_user_finalize (GObject *object)
1339 LightDMUser *self = LIGHTDM_USER (object);
1340 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1342 g_free (priv->name);
1343 g_free (priv->real_name);
1344 g_free (priv->home_directory);
1345 g_free (priv->image);
1346 if (priv->dmrc_file)
1347 g_key_file_free (priv->dmrc_file);
1351 lightdm_user_class_init (LightDMUserClass *klass)
1353 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1355 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1357 object_class->set_property = lightdm_user_set_property;
1358 object_class->get_property = lightdm_user_get_property;
1359 object_class->finalize = lightdm_user_finalize;
1361 g_object_class_install_property (object_class,
1363 g_param_spec_string ("name",
1367 G_PARAM_READWRITE));
1368 g_object_class_install_property (object_class,
1369 USER_PROP_REAL_NAME,
1370 g_param_spec_string ("real-name",
1374 G_PARAM_READWRITE));
1375 g_object_class_install_property (object_class,
1376 USER_PROP_DISPLAY_NAME,
1377 g_param_spec_string ("display-name",
1379 "Users display name",
1382 g_object_class_install_property (object_class,
1383 USER_PROP_HOME_DIRECTORY,
1384 g_param_spec_string ("home-directory",
1388 G_PARAM_READWRITE));
1389 g_object_class_install_property (object_class,
1391 g_param_spec_string ("image",
1395 G_PARAM_READWRITE));
1396 g_object_class_install_property (object_class,
1398 g_param_spec_string ("language",
1400 "Language used by this user",
1403 g_object_class_install_property (object_class,
1405 g_param_spec_string ("layout",
1407 "Keyboard layout used by this user",
1410 g_object_class_install_property (object_class,
1412 g_param_spec_string ("session",
1414 "Session used by this user",
1417 g_object_class_install_property (object_class,
1418 USER_PROP_LOGGED_IN,
1419 g_param_spec_boolean ("logged-in",
1421 "TRUE if the user is currently in a session",
1423 G_PARAM_READWRITE));
1426 * LightDMUser::changed:
1427 * @user: A #LightDMUser
1429 * The ::changed signal gets emitted this user account is modified.
1431 user_signals[CHANGED] =
1432 g_signal_new ("changed",
1433 G_TYPE_FROM_CLASS (klass),
1435 G_STRUCT_OFFSET (LightDMUserClass, changed),
1437 g_cclosure_marshal_VOID__VOID,
1442 session_init (Session *session)
1447 session_finalize (GObject *object)
1449 Session *self = SESSION (object);
1451 g_free (self->path);
1452 g_free (self->username);
1456 session_class_init (SessionClass *klass)
1458 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1459 object_class->finalize = session_finalize;