1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
3 * Copyright (C) 2010 Robert Ancell.
4 * Copyright (C) 2014 Canonical, Ltd.
5 * Authors: Robert Ancell <robert.ancell@canonical.com>
6 * Michael Terry <michael.terry@canonical.com>
8 * This library is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 2 or version 3 of the License.
11 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
18 #include <sys/utsname.h>
22 #include "user-list.h"
36 USER_PROP_DISPLAY_NAME,
37 USER_PROP_HOME_DIRECTORY,
45 USER_PROP_HAS_MESSAGES
55 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
62 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
66 /* Bus connection being communicated on */
69 /* D-Bus signals for accounts service events */
70 guint user_added_signal;
71 guint user_removed_signal;
73 /* D-Bus signals for display manager events */
74 guint session_added_signal;
75 guint session_removed_signal;
77 /* File monitor for password file */
78 GFileMonitor *passwd_monitor;
80 /* TRUE if have scanned users */
86 /* List of sessions */
88 } CommonUserListPrivate;
92 /* User list this user is part of */
93 CommonUserList *user_list;
95 /* TRUE if have loaded user properties */
96 gboolean loaded_values;
98 /* Accounts service path */
104 /* Update signal from accounts service */
105 guint changed_signal;
110 /* Descriptive name for user */
113 /* Home directory of user */
114 gchar *home_directory;
119 /* Background image for users */
122 /* TRUE if this user has messages available */
123 gboolean has_messages;
125 /* User chosen language */
128 /* User layout preferences */
131 /* User default session */
137 GObject parent_instance;
144 GObjectClass parent_class;
147 G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
148 G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
149 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
150 GType session_get_type (void);
151 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
153 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
154 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
156 #define PASSWD_FILE "/etc/passwd"
157 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
159 static CommonUserList *singleton = NULL;
162 * common_user_list_get_instance:
166 * Return value: (transfer none): the #CommonUserList
169 common_user_list_get_instance (void)
172 singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
177 get_user_by_name (CommonUserList *user_list, const gchar *username)
179 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
182 for (link = priv->users; link; link = link->next)
184 CommonUser *user = link->data;
185 if (g_strcmp0 (common_user_get_name (user), username) == 0)
193 get_user_by_path (CommonUserList *user_list, const gchar *path)
195 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
198 for (link = priv->users; link; link = link->next)
200 CommonUser *user = link->data;
201 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
209 compare_user (gconstpointer a, gconstpointer b)
211 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
212 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
216 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
218 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
220 /* Skip if already set to this */
221 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
222 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
223 g_strcmp0 (common_user_get_image (user), image) == 0)
226 g_free (priv->real_name);
227 priv->real_name = g_strdup (real_name);
228 g_free (priv->home_directory);
229 priv->home_directory = g_strdup (home_directory);
230 g_free (priv->image);
231 priv->image = g_strdup (image);
237 user_changed_cb (CommonUser *user)
239 g_signal_emit (GET_USER_PRIVATE (user)->user_list, list_signals[USER_CHANGED], 0, user);
243 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
245 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
249 gchar **hidden_users, **hidden_shells;
250 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
251 GError *error = NULL;
253 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
255 config = g_key_file_new ();
256 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
257 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
258 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
259 g_clear_error (&error);
261 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
262 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
266 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
268 value = g_strdup ("nobody nobody4 noaccess");
269 hidden_users = g_strsplit (value, " ", -1);
272 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
274 value = g_strdup ("/bin/false /usr/sbin/nologin");
275 hidden_shells = g_strsplit (value, " ", -1);
278 g_key_file_free (config);
284 struct passwd *entry;
286 CommonUserPrivate *user_priv;
288 gchar *real_name, *image;
296 /* Ignore system users */
297 if (entry->pw_uid < minimum_uid)
300 /* Ignore users disabled by shell */
303 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
304 if (hidden_shells[i])
308 /* Ignore certain users */
309 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
313 tokens = g_strsplit (entry->pw_gecos, ",", -1);
314 if (tokens[0] != NULL && tokens[0][0] != '\0')
315 real_name = g_strdup (tokens[0]);
317 real_name = g_strdup ("");
320 image = g_build_filename (entry->pw_dir, ".face", NULL);
321 if (!g_file_test (image, G_FILE_TEST_EXISTS))
324 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
325 if (!g_file_test (image, G_FILE_TEST_EXISTS))
332 user = g_object_new (COMMON_TYPE_USER, NULL);
333 user_priv = GET_USER_PRIVATE (user);
334 user_priv->user_list = user_list;
335 g_free (user_priv->name);
336 user_priv->name = g_strdup (entry->pw_name);
337 g_free (user_priv->real_name);
338 user_priv->real_name = real_name;
339 g_free (user_priv->home_directory);
340 user_priv->home_directory = g_strdup (entry->pw_dir);
341 g_free (user_priv->image);
342 user_priv->image = image;
344 /* Update existing users if have them */
345 for (link = priv->users; link; link = link->next)
347 CommonUser *info = link->data;
348 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
350 if (update_passwd_user (info, common_user_get_real_name (user), common_user_get_home_directory (user), common_user_get_image (user)))
351 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
352 g_object_unref (user);
359 /* Only notify once we have loaded the user list */
360 if (priv->have_users)
361 new_users = g_list_insert_sorted (new_users, user, compare_user);
363 users = g_list_insert_sorted (users, user, compare_user);
365 g_strfreev (hidden_users);
366 g_strfreev (hidden_shells);
369 g_warning ("Failed to read password database: %s", strerror (errno));
373 /* Use new user list */
374 old_users = priv->users;
377 /* Notify of changes */
378 for (link = new_users; link; link = link->next)
380 CommonUser *info = link->data;
381 g_debug ("User %s added", common_user_get_name (info));
382 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), NULL);
384 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
386 g_list_free (new_users);
387 for (link = changed_users; link; link = link->next)
389 CommonUser *info = link->data;
390 g_debug ("User %s changed", common_user_get_name (info));
391 g_signal_emit (info, user_signals[CHANGED], 0);
393 g_list_free (changed_users);
394 for (link = old_users; link; link = link->next)
398 /* See if this user is in the current list */
399 for (new_link = priv->users; new_link; new_link = new_link->next)
401 if (new_link->data == link->data)
407 CommonUser *info = link->data;
408 g_debug ("User %s removed", common_user_get_name (info));
409 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
410 g_object_unref (info);
413 g_list_free (old_users);
417 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
419 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
421 g_debug ("%s changed, reloading user list", g_file_get_path (file));
422 load_passwd_file (user_list, TRUE);
426 static gboolean load_accounts_user (CommonUser *user);
429 accounts_user_changed_cb (GDBusConnection *connection,
430 const gchar *sender_name,
431 const gchar *object_path,
432 const gchar *interface_name,
433 const gchar *signal_name,
434 GVariant *parameters,
437 CommonUser *user = data;
438 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
440 g_debug ("User %s changed", priv->path);
441 if (load_accounts_user (user))
442 g_signal_emit (user, user_signals[CHANGED], 0);
446 load_accounts_user (CommonUser *user)
448 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
449 GVariant *result, *value;
452 gboolean system_account = FALSE;
453 GError *error = NULL;
455 /* Get the properties for this user */
456 if (!priv->changed_signal)
457 priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
458 "org.freedesktop.Accounts",
459 "org.freedesktop.Accounts.User",
463 G_DBUS_SIGNAL_FLAGS_NONE,
464 accounts_user_changed_cb,
467 result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
468 "org.freedesktop.Accounts",
470 "org.freedesktop.DBus.Properties",
472 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
473 G_VARIANT_TYPE ("(a{sv})"),
474 G_DBUS_CALL_FLAGS_NONE,
479 g_warning ("Error updating user %s: %s", priv->path, error->message);
480 g_clear_error (&error);
484 /* Store the properties we need */
485 g_variant_get (result, "(a{sv})", &iter);
486 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
488 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
491 priv->name = g_variant_dup_string (value, NULL);
493 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
495 g_free (priv->real_name);
496 priv->real_name = g_variant_dup_string (value, NULL);
498 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
500 g_free (priv->home_directory);
501 priv->home_directory = g_variant_dup_string (value, NULL);
503 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
504 system_account = g_variant_get_boolean (value);
505 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
508 g_free (priv->language);
509 priv->language = g_variant_dup_string (value, NULL);
511 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
513 g_free (priv->image);
514 priv->image = g_variant_dup_string (value, NULL);
515 if (strcmp (priv->image, "") == 0)
517 g_free (priv->image);
521 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
523 g_free (priv->session);
524 priv->session = g_variant_dup_string (value, NULL);
526 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
528 g_free (priv->background);
529 priv->background = g_variant_dup_string (value, NULL);
530 if (strcmp (priv->background, "") == 0)
532 g_free (priv->background);
533 priv->background = NULL;
536 else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
538 g_strfreev (priv->layouts);
539 priv->layouts = g_variant_dup_strv (value, NULL);
542 priv->layouts = g_malloc (sizeof (gchar *) * 1);
543 priv->layouts[0] = NULL;
546 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
547 priv->has_messages = g_variant_get_boolean (value);
549 g_variant_iter_free (iter);
551 g_variant_unref (result);
553 priv->loaded_values = TRUE;
555 return !system_account;
559 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
561 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
563 CommonUserPrivate *priv;
565 user = g_object_new (COMMON_TYPE_USER, NULL);
566 priv = GET_USER_PRIVATE (user);
568 g_debug ("User %s added", path);
569 priv->user_list = user_list;
570 priv->path = g_strdup (path);
571 g_signal_connect (user, "changed", G_CALLBACK (user_changed_cb), NULL);
572 if (load_accounts_user (user))
574 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
576 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
579 g_object_unref (user);
583 accounts_user_added_cb (GDBusConnection *connection,
584 const gchar *sender_name,
585 const gchar *object_path,
586 const gchar *interface_name,
587 const gchar *signal_name,
588 GVariant *parameters,
591 CommonUserList *user_list = data;
595 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
597 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
601 g_variant_get (parameters, "(&o)", &path);
603 /* Add user if we haven't got them */
604 user = get_user_by_path (user_list, path);
606 add_accounts_user (user_list, path, TRUE);
610 accounts_user_deleted_cb (GDBusConnection *connection,
611 const gchar *sender_name,
612 const gchar *object_path,
613 const gchar *interface_name,
614 const gchar *signal_name,
615 GVariant *parameters,
618 CommonUserList *user_list = data;
619 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
623 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
625 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
629 g_variant_get (parameters, "(&o)", &path);
631 /* Delete user if we know of them */
632 user = get_user_by_path (user_list, path);
635 g_debug ("User %s deleted", path);
636 priv->users = g_list_remove (priv->users, user);
638 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
640 g_object_unref (user);
645 load_session (CommonUserList *user_list, const gchar *path)
647 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
648 Session *session = NULL;
649 GVariant *result, *username;
650 GError *error = NULL;
652 result = g_dbus_connection_call_sync (priv->bus,
653 "org.freedesktop.DisplayManager",
655 "org.freedesktop.DBus.Properties",
657 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
658 G_VARIANT_TYPE ("(v)"),
659 G_DBUS_CALL_FLAGS_NONE,
664 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
665 g_clear_error (&error);
669 g_variant_get (result, "(v)", &username);
670 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
674 g_variant_get (username, "&s", &name);
676 g_debug ("Loaded session %s (%s)", path, name);
677 session = g_object_new (session_get_type (), NULL);
678 session->username = g_strdup (name);
679 session->path = g_strdup (path);
680 priv->sessions = g_list_append (priv->sessions, session);
682 g_variant_unref (username);
683 g_variant_unref (result);
689 session_added_cb (GDBusConnection *connection,
690 const gchar *sender_name,
691 const gchar *object_path,
692 const gchar *interface_name,
693 const gchar *signal_name,
694 GVariant *parameters,
697 CommonUserList *user_list = data;
700 CommonUser *user = NULL;
702 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
704 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
708 g_variant_get (parameters, "(&o)", &path);
709 session = load_session (user_list, path);
711 user = get_user_by_name (user_list, session->username);
713 g_signal_emit (user, user_signals[CHANGED], 0);
717 session_removed_cb (GDBusConnection *connection,
718 const gchar *sender_name,
719 const gchar *object_path,
720 const gchar *interface_name,
721 const gchar *signal_name,
722 GVariant *parameters,
725 CommonUserList *user_list = data;
726 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
730 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
732 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
736 g_variant_get (parameters, "(&o)", &path);
738 for (link = priv->sessions; link; link = link->next)
740 Session *session = link->data;
741 if (strcmp (session->path, path) == 0)
745 g_debug ("Session %s removed", path);
746 priv->sessions = g_list_remove_link (priv->sessions, link);
747 user = get_user_by_name (user_list, session->username);
749 g_signal_emit (user, user_signals[CHANGED], 0);
750 g_object_unref (session);
757 load_users (CommonUserList *user_list)
759 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
761 GError *error = NULL;
763 if (priv->have_users)
765 priv->have_users = TRUE;
767 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
768 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
769 "org.freedesktop.Accounts",
770 "org.freedesktop.Accounts",
772 "/org/freedesktop/Accounts",
774 G_DBUS_SIGNAL_FLAGS_NONE,
775 accounts_user_added_cb,
778 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
779 "org.freedesktop.Accounts",
780 "org.freedesktop.Accounts",
782 "/org/freedesktop/Accounts",
784 G_DBUS_SIGNAL_FLAGS_NONE,
785 accounts_user_deleted_cb,
788 result = g_dbus_connection_call_sync (priv->bus,
789 "org.freedesktop.Accounts",
790 "/org/freedesktop/Accounts",
791 "org.freedesktop.Accounts",
793 g_variant_new ("()"),
794 G_VARIANT_TYPE ("(ao)"),
795 G_DBUS_CALL_FLAGS_NONE,
800 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
801 g_clear_error (&error);
807 g_debug ("Loading users from org.freedesktop.Accounts");
808 g_variant_get (result, "(ao)", &iter);
809 while (g_variant_iter_loop (iter, "&o", &path))
810 add_accounts_user (user_list, path, FALSE);
811 g_variant_iter_free (iter);
812 g_variant_unref (result);
818 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
819 priv->user_added_signal = 0;
820 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
821 priv->user_removed_signal = 0;
823 load_passwd_file (user_list, FALSE);
825 /* Watch for changes to user list */
827 passwd_file = g_file_new_for_path (PASSWD_FILE);
828 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
829 g_object_unref (passwd_file);
831 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
833 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
834 g_clear_error (&error);
837 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
838 "org.freedesktop.DisplayManager",
839 "org.freedesktop.DisplayManager",
841 "/org/freedesktop/DisplayManager",
843 G_DBUS_SIGNAL_FLAGS_NONE,
847 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
848 "org.freedesktop.DisplayManager",
849 "org.freedesktop.DisplayManager",
851 "/org/freedesktop/DisplayManager",
853 G_DBUS_SIGNAL_FLAGS_NONE,
858 result = g_dbus_connection_call_sync (priv->bus,
859 "org.freedesktop.DisplayManager",
860 "/org/freedesktop/DisplayManager",
861 "org.freedesktop.DBus.Properties",
863 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
864 G_VARIANT_TYPE ("(v)"),
865 G_DBUS_CALL_FLAGS_NONE,
870 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
871 g_clear_error (&error);
874 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
880 g_variant_get (result, "(v)", &value);
882 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
883 g_variant_get (value, "ao", &iter);
884 while (g_variant_iter_loop (iter, "&o", &path))
885 load_session (user_list, path);
886 g_variant_iter_free (iter);
888 g_variant_unref (value);
891 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
893 g_variant_unref (result);
898 * common_user_list_get_length:
899 * @user_list: a #CommonUserList
901 * Return value: The number of users able to log in
904 common_user_list_get_length (CommonUserList *user_list)
906 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
907 load_users (user_list);
908 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
912 * common_user_list_get_users:
913 * @user_list: A #CommonUserList
915 * Get a list of users to present to the user. This list may be a subset of the
916 * available users and may be empty depending on the server configuration.
918 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
921 common_user_list_get_users (CommonUserList *user_list)
923 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
924 load_users (user_list);
925 return GET_LIST_PRIVATE (user_list)->users;
929 * common_user_list_get_user_by_name:
930 * @user_list: A #CommonUserList
931 * @username: Name of user to get.
933 * Get infomation about a given user or #NULL if this user doesn't exist.
935 * Return value: (transfer none): A #CommonUser entry for the given user.
938 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
940 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
941 g_return_val_if_fail (username != NULL, NULL);
943 load_users (user_list);
945 return get_user_by_name (user_list, username);
949 common_user_list_init (CommonUserList *user_list)
951 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
953 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
957 common_user_list_set_property (GObject *object,
962 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
966 common_user_list_get_property (GObject *object,
971 CommonUserList *self;
973 self = COMMON_USER_LIST (object);
977 case LIST_PROP_NUM_USERS:
978 g_value_set_int (value, common_user_list_get_length (self));
981 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
987 common_user_list_finalize (GObject *object)
989 CommonUserList *self = COMMON_USER_LIST (object);
990 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
992 /* Remove children first, they might access us */
993 g_list_free_full (priv->users, g_object_unref);
994 g_list_free_full (priv->sessions, g_object_unref);
996 if (priv->user_added_signal)
997 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
998 if (priv->user_removed_signal)
999 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1000 if (priv->session_added_signal)
1001 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1002 if (priv->session_removed_signal)
1003 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1004 g_object_unref (priv->bus);
1005 if (priv->passwd_monitor)
1006 g_object_unref (priv->passwd_monitor);
1008 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1012 common_user_list_class_init (CommonUserListClass *klass)
1014 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1016 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1018 object_class->set_property = common_user_list_set_property;
1019 object_class->get_property = common_user_list_get_property;
1020 object_class->finalize = common_user_list_finalize;
1022 g_object_class_install_property (object_class,
1023 LIST_PROP_NUM_USERS,
1024 g_param_spec_int ("num-users",
1026 "Number of login users",
1030 * CommonUserList::user-added:
1031 * @user_list: A #CommonUserList
1032 * @user: The #CommonUser that has been added.
1034 * The ::user-added signal gets emitted when a user account is created.
1036 list_signals[USER_ADDED] =
1037 g_signal_new ("user-added",
1038 G_TYPE_FROM_CLASS (klass),
1040 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1043 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1046 * CommonUserList::user-changed:
1047 * @user_list: A #CommonUserList
1048 * @user: The #CommonUser that has been changed.
1050 * The ::user-changed signal gets emitted when a user account is modified.
1052 list_signals[USER_CHANGED] =
1053 g_signal_new ("user-changed",
1054 G_TYPE_FROM_CLASS (klass),
1056 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1059 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1062 * CommonUserList::user-removed:
1063 * @user_list: A #CommonUserList
1064 * @user: The #CommonUser that has been removed.
1066 * The ::user-removed signal gets emitted when a user account is removed.
1068 list_signals[USER_REMOVED] =
1069 g_signal_new ("user-removed",
1070 G_TYPE_FROM_CLASS (klass),
1072 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1075 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1079 load_dmrc (CommonUser *user)
1081 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1083 //gboolean have_dmrc;
1085 if (!priv->dmrc_file)
1086 priv->dmrc_file = g_key_file_new ();
1088 /* Load from the user directory */
1089 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1090 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1093 /* If no ~/.dmrc, then load from the cache */
1096 // FIXME: Watch for changes
1098 /* The Language field contains the locale */
1100 g_free (priv->language);
1101 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1103 if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1105 g_strfreev (priv->layouts);
1106 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1107 priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1108 priv->layouts[1] = NULL;
1112 g_free (priv->session);
1113 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1116 /* Loads language/layout/session info for user */
1118 load_user_values (CommonUser *user)
1120 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1122 if (priv->loaded_values)
1124 priv->loaded_values = TRUE;
1131 * common_user_get_name:
1132 * @user: A #CommonUser
1134 * Get the name of a user.
1136 * Return value: The name of the given user
1139 common_user_get_name (CommonUser *user)
1141 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1142 load_user_values (user);
1143 return GET_USER_PRIVATE (user)->name;
1147 * common_user_get_real_name:
1148 * @user: A #CommonUser
1150 * Get the real name of a user.
1152 * Return value: The real name of the given user
1155 common_user_get_real_name (CommonUser *user)
1157 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1158 load_user_values (user);
1159 return GET_USER_PRIVATE (user)->real_name;
1163 * common_user_get_display_name:
1164 * @user: A #CommonUser
1166 * Get the display name of a user.
1168 * Return value: The display name of the given user
1171 common_user_get_display_name (CommonUser *user)
1173 CommonUserPrivate *priv;
1175 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1177 load_user_values (user);
1179 priv = GET_USER_PRIVATE (user);
1180 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1183 return priv->real_name;
1187 * common_user_get_home_directory:
1188 * @user: A #CommonUser
1190 * Get the home directory for a user.
1192 * Return value: The users home directory
1195 common_user_get_home_directory (CommonUser *user)
1197 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1198 load_user_values (user);
1199 return GET_USER_PRIVATE (user)->home_directory;
1203 * common_user_get_image:
1204 * @user: A #CommonUser
1206 * Get the image URI for a user.
1208 * Return value: The image URI for the given user or #NULL if no URI
1211 common_user_get_image (CommonUser *user)
1213 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1214 load_user_values (user);
1215 return GET_USER_PRIVATE (user)->image;
1219 * common_user_get_background:
1220 * @user: A #CommonUser
1222 * Get the background file path for a user.
1224 * Return value: The background file path for the given user or #NULL if no path
1227 common_user_get_background (CommonUser *user)
1229 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1230 load_user_values (user);
1231 return GET_USER_PRIVATE (user)->background;
1235 * common_user_get_language:
1236 * @user: A #CommonUser
1238 * Get the language for a user.
1240 * 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.
1243 common_user_get_language (CommonUser *user)
1245 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1246 load_user_values (user);
1247 return GET_USER_PRIVATE (user)->language;
1251 * common_user_get_layout:
1252 * @user: A #CommonUser
1254 * Get the keyboard layout for a user.
1256 * 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.
1259 common_user_get_layout (CommonUser *user)
1261 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1262 load_user_values (user);
1263 return GET_USER_PRIVATE (user)->layouts[0];
1267 * common_user_get_layouts:
1268 * @user: A #CommonUser
1270 * Get the configured keyboard layouts for a user.
1272 * 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.
1274 const gchar * const *
1275 common_user_get_layouts (CommonUser *user)
1277 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1278 load_user_values (user);
1279 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1283 * common_user_get_session:
1284 * @user: A #CommonUser
1286 * Get the session for a user.
1288 * Return value: The session for the given user or #NULL if using system defaults.
1291 common_user_get_session (CommonUser *user)
1293 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1294 load_user_values (user);
1295 return GET_USER_PRIVATE (user)->session;
1299 * common_user_get_logged_in:
1300 * @user: A #CommonUser
1302 * Check if a user is logged in.
1304 * Return value: #TRUE if the user is currently logged in.
1307 common_user_get_logged_in (CommonUser *user)
1309 CommonUserPrivate *priv;
1310 CommonUserListPrivate *list_priv;
1313 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1315 priv = GET_USER_PRIVATE (user);
1316 list_priv = GET_LIST_PRIVATE (priv->user_list);
1318 for (link = list_priv->sessions; link; link = link->next)
1320 Session *session = link->data;
1321 if (strcmp (session->username, priv->name) == 0)
1329 * common_user_get_has_messages:
1330 * @user: A #CommonUser
1332 * Check if a user has waiting messages.
1334 * Return value: #TRUE if the user has waiting messages.
1337 common_user_get_has_messages (CommonUser *user)
1339 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1340 load_user_values (user);
1341 return GET_USER_PRIVATE (user)->has_messages;
1345 common_user_init (CommonUser *user)
1347 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1348 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1349 priv->layouts[0] = NULL;
1353 common_user_set_property (GObject *object,
1355 const GValue *value,
1358 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1362 common_user_get_property (GObject *object,
1369 self = COMMON_USER (object);
1373 case USER_PROP_NAME:
1374 g_value_set_string (value, common_user_get_name (self));
1376 case USER_PROP_REAL_NAME:
1377 g_value_set_string (value, common_user_get_real_name (self));
1379 case USER_PROP_DISPLAY_NAME:
1380 g_value_set_string (value, common_user_get_display_name (self));
1382 case USER_PROP_HOME_DIRECTORY:
1383 g_value_set_string (value, common_user_get_home_directory (self));
1385 case USER_PROP_IMAGE:
1386 g_value_set_string (value, common_user_get_image (self));
1388 case USER_PROP_BACKGROUND:
1389 g_value_set_string (value, common_user_get_background (self));
1391 case USER_PROP_LANGUAGE:
1392 g_value_set_string (value, common_user_get_language (self));
1394 case USER_PROP_LAYOUT:
1395 g_value_set_string (value, common_user_get_layout (self));
1397 case USER_PROP_LAYOUTS:
1398 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1400 case USER_PROP_SESSION:
1401 g_value_set_string (value, common_user_get_session (self));
1403 case USER_PROP_LOGGED_IN:
1404 g_value_set_boolean (value, common_user_get_logged_in (self));
1406 case USER_PROP_HAS_MESSAGES:
1407 g_value_set_boolean (value, common_user_get_has_messages (self));
1410 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1416 common_user_finalize (GObject *object)
1418 CommonUser *self = COMMON_USER (object);
1419 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1421 g_free (priv->path);
1422 if (priv->changed_signal)
1423 g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1424 g_free (priv->name);
1425 g_free (priv->real_name);
1426 g_free (priv->home_directory);
1427 g_free (priv->image);
1428 g_free (priv->background);
1429 g_strfreev (priv->layouts);
1430 if (priv->dmrc_file)
1431 g_key_file_free (priv->dmrc_file);
1435 common_user_class_init (CommonUserClass *klass)
1437 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1439 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1441 object_class->set_property = common_user_set_property;
1442 object_class->get_property = common_user_get_property;
1443 object_class->finalize = common_user_finalize;
1445 g_object_class_install_property (object_class,
1447 g_param_spec_string ("name",
1451 G_PARAM_READWRITE));
1452 g_object_class_install_property (object_class,
1453 USER_PROP_REAL_NAME,
1454 g_param_spec_string ("real-name",
1458 G_PARAM_READWRITE));
1459 g_object_class_install_property (object_class,
1460 USER_PROP_DISPLAY_NAME,
1461 g_param_spec_string ("display-name",
1463 "Users display name",
1466 g_object_class_install_property (object_class,
1467 USER_PROP_HOME_DIRECTORY,
1468 g_param_spec_string ("home-directory",
1472 G_PARAM_READWRITE));
1473 g_object_class_install_property (object_class,
1475 g_param_spec_string ("image",
1479 G_PARAM_READWRITE));
1480 g_object_class_install_property (object_class,
1481 USER_PROP_BACKGROUND,
1482 g_param_spec_string ("background",
1486 G_PARAM_READWRITE));
1487 g_object_class_install_property (object_class,
1489 g_param_spec_string ("language",
1491 "Language used by this user",
1494 g_object_class_install_property (object_class,
1496 g_param_spec_string ("layout",
1498 "Keyboard layout used by this user",
1501 g_object_class_install_property (object_class,
1503 g_param_spec_boxed ("layouts",
1505 "Keyboard layouts used by this user",
1508 g_object_class_install_property (object_class,
1510 g_param_spec_string ("session",
1512 "Session used by this user",
1515 g_object_class_install_property (object_class,
1516 USER_PROP_LOGGED_IN,
1517 g_param_spec_boolean ("logged-in",
1519 "TRUE if the user is currently in a session",
1521 G_PARAM_READWRITE));
1522 g_object_class_install_property (object_class,
1523 USER_PROP_LOGGED_IN,
1524 g_param_spec_boolean ("has-messages",
1526 "TRUE if the user is has waiting messages",
1528 G_PARAM_READWRITE));
1531 * CommonUser::changed:
1532 * @user: A #CommonUser
1534 * The ::changed signal gets emitted this user account is modified.
1536 user_signals[CHANGED] =
1537 g_signal_new ("changed",
1538 G_TYPE_FROM_CLASS (klass),
1540 G_STRUCT_OFFSET (CommonUserClass, changed),
1547 session_init (Session *session)
1552 session_finalize (GObject *object)
1554 Session *self = SESSION (object);
1556 g_free (self->path);
1557 g_free (self->username);
1561 session_class_init (SessionClass *klass)
1563 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1564 object_class->finalize = session_finalize;