aboutsummaryrefslogtreecommitdiff
path: root/src/backend-dbus
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend-dbus')
-rw-r--r--src/backend-dbus/CMakeLists.txt57
-rw-r--r--src/backend-dbus/actions.c970
-rw-r--r--src/backend-dbus/actions.h71
-rw-r--r--src/backend-dbus/backend-dbus.c115
-rw-r--r--src/backend-dbus/backend-dbus.h38
-rw-r--r--src/backend-dbus/com.canonical.indicators.webcredentials.xml82
-rw-r--r--src/backend-dbus/guest.c394
-rw-r--r--src/backend-dbus/guest.h70
-rw-r--r--src/backend-dbus/org.freedesktop.Accounts.User.xml744
-rw-r--r--src/backend-dbus/org.freedesktop.Accounts.xml194
-rw-r--r--src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml30
-rw-r--r--src/backend-dbus/org.freedesktop.login1.Manager.xml199
-rw-r--r--src/backend-dbus/org.freedesktop.login1.Seat.xml21
-rw-r--r--src/backend-dbus/org.freedesktop.login1.User.xml56
-rw-r--r--src/backend-dbus/org.gnome.ScreenSaver.xml16
-rw-r--r--src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml53
-rw-r--r--src/backend-dbus/org.gnome.SessionManager.xml451
-rw-r--r--src/backend-dbus/users.c706
-rw-r--r--src/backend-dbus/users.h73
-rw-r--r--src/backend-dbus/utils.c182
-rw-r--r--src/backend-dbus/utils.h49
21 files changed, 4571 insertions, 0 deletions
diff --git a/src/backend-dbus/CMakeLists.txt b/src/backend-dbus/CMakeLists.txt
new file mode 100644
index 0000000..fa41534
--- /dev/null
+++ b/src/backend-dbus/CMakeLists.txt
@@ -0,0 +1,57 @@
+include (GdbusCodegen)
+
+set(BACKEND_GENERATED_SOURCES
+)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-display-manager
+ org.freedesktop
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.DisplayManager.Seat.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-webcredentials
+ com.canonical.indicators
+ ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicators.webcredentials.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-accounts
+ org.freedesktop
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.Accounts.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-user
+ org.freedesktop
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.Accounts.User.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-manager
+ org.freedesktop
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Manager.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-seat
+ org.freedesktop
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Seat.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-user
+ org.freedesktop
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.User.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES gnome-screen-saver
+ org
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.ScreenSaver.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES gnome-session-manager
+ org
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.SessionManager.xml)
+
+add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-end-session-dialog
+ org.gnome.SessionManager
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.SessionManager.EndSessionDialog.xml)
+
+set (SOURCES actions.c guest.c users.c backend-dbus.c utils.c)
+
+# add warnings/coverage info on handwritten files
+# but not the autogenerated ones...
+set_source_files_properties (${SOURCES}
+ PROPERTIES COMPILE_FLAGS " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}")
+
+# add the bin dir to our include path s.t. our code can find the autogenerated header files
+include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS})
+
+add_library (backenddbus STATIC ${SOURCES} ${BACKEND_GENERATED_SOURCES})
+
diff --git a/src/backend-dbus/actions.c b/src/backend-dbus/actions.c
new file mode 100644
index 0000000..9030ca7
--- /dev/null
+++ b/src/backend-dbus/actions.c
@@ -0,0 +1,970 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "dbus-end-session-dialog.h"
+#include "dbus-login1-manager.h"
+#include "dbus-webcredentials.h"
+#include "gnome-screen-saver.h"
+#include "gnome-session-manager.h"
+
+#include "actions.h"
+
+enum
+{
+ END_SESSION_TYPE_LOGOUT = 0,
+ END_SESSION_TYPE_SHUTDOWN,
+ END_SESSION_TYPE_REBOOT
+};
+
+struct _IndicatorSessionActionsDbusPriv
+{
+ GCancellable * cancellable;
+
+ GSettings * lockdown_settings;
+ GSettings * indicator_settings;
+ GnomeScreenSaver * screen_saver;
+ GnomeSessionManager * session_manager;
+ Login1Manager * login1_manager;
+ GCancellable * login1_manager_cancellable;
+ Login1Seat * login1_seat;
+ DisplayManagerSeat * dm_seat;
+ GCancellable * dm_seat_cancellable;
+ Webcredentials * webcredentials;
+ EndSessionDialog * end_session_dialog;
+ char * zenity;
+
+ gboolean can_suspend;
+ gboolean can_hibernate;
+ gboolean seat_allows_activation;
+};
+
+typedef IndicatorSessionActionsDbusPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorSessionActionsDbus,
+ indicator_session_actions_dbus,
+ INDICATOR_TYPE_SESSION_ACTIONS)
+
+/***
+****
+***/
+
+static void
+log_and_clear_error (GError ** err, const char * loc, const char * func)
+{
+ if (*err)
+ {
+ if (!g_error_matches (*err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s %s: %s", loc, func, (*err)->message);
+
+ g_clear_error (err);
+ }
+}
+
+/***
+****
+***/
+
+typedef enum
+{
+ PROMPT_NONE,
+ PROMPT_WITH_ZENITY,
+ PROMPT_WITH_UNITY
+}
+prompt_status_t;
+
+static prompt_status_t
+get_prompt_status (IndicatorSessionActionsDbus * self)
+{
+ prompt_status_t prompt = PROMPT_NONE;
+ const priv_t * p = self->priv;
+
+ if (!g_settings_get_boolean (p->indicator_settings, "suppress-logout-restart-shutdown"))
+ {
+ /* can we use the Unity prompt? */
+ if ((prompt == PROMPT_NONE) && p && p->end_session_dialog)
+ {
+ GDBusProxy * proxy = G_DBUS_PROXY (p->end_session_dialog);
+ char * name = g_dbus_proxy_get_name_owner (proxy);
+ if (name != NULL)
+ prompt = PROMPT_WITH_UNITY;
+ g_free (name);
+ }
+
+ /* can we use zenity? */
+ if ((prompt == PROMPT_NONE) && p && p->zenity)
+ prompt = PROMPT_WITH_ZENITY;
+ }
+
+ return prompt;
+}
+
+/***
+****
+***/
+
+static void
+on_seat_notify_multi_session (IndicatorSessionActionsDbus * self)
+{
+ priv_t * p = self->priv;
+ gboolean b;
+
+ b = login1_seat_get_can_multi_session (p->login1_seat);
+
+ if (p->seat_allows_activation != b)
+ {
+ p->seat_allows_activation = b;
+
+ indicator_session_actions_notify_can_switch (INDICATOR_SESSION_ACTIONS(self));
+ }
+}
+
+static void
+set_login1_seat (IndicatorSessionActionsDbus * self, Login1Seat * seat)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_seat != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_seat, self);
+ g_clear_object (&p->login1_seat);
+ }
+
+ if (seat != NULL)
+ {
+ p->login1_seat = g_object_ref (seat);
+
+ g_signal_connect_swapped (seat, "notify::can-multi-session",
+ G_CALLBACK(on_seat_notify_multi_session), self);
+ }
+}
+
+/***
+****
+***/
+
+static void
+set_dm_seat (IndicatorSessionActionsDbus * self, DisplayManagerSeat * seat)
+{
+ priv_t * p = self->priv;
+
+ if (p->dm_seat != NULL)
+ {
+ g_cancellable_cancel (p->dm_seat_cancellable);
+ g_clear_object (&p->dm_seat);
+ g_clear_object (&p->dm_seat);
+ }
+
+ if (seat != NULL)
+ {
+ p->dm_seat = g_object_ref (seat);
+ p->dm_seat_cancellable = g_cancellable_new ();
+ }
+}
+
+static void
+on_screensaver_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+{
+ GError * err;
+ GnomeScreenSaver * ss;
+
+ err = NULL;
+ ss = gnome_screen_saver_proxy_new_for_bus_finish (res, &err);
+ if (err == NULL)
+ {
+ INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->screen_saver = ss;
+ }
+
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+on_can_suspend_ready (GObject * o, GAsyncResult * res, gpointer gself)
+{
+ char * str;
+ GError * err;
+
+ str = NULL;
+ err = NULL;
+ login1_manager_call_can_suspend_finish (LOGIN1_MANAGER(o), &str, res, &err);
+ if (err == NULL)
+ {
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv;
+
+ const gboolean b = !g_strcmp0 (str, "yes");
+
+ if (p->can_suspend != b)
+ {
+ p->can_suspend = b;
+ indicator_session_actions_notify_can_suspend (gself);
+ }
+
+ g_free (str);
+ }
+
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+on_can_hibernate_ready (GObject * o, GAsyncResult * res, gpointer gself)
+{
+ gchar * str;
+ GError * err;
+
+ str = NULL;
+ err = NULL;
+ login1_manager_call_can_hibernate_finish (LOGIN1_MANAGER(o), &str, res, &err);
+ if (err == NULL)
+ {
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv;
+
+ const gboolean b = !g_strcmp0 (str, "yes");
+
+ if (p->can_hibernate != b)
+ {
+ p->can_hibernate = b;
+ indicator_session_actions_notify_can_hibernate (gself);
+ }
+
+ g_free (str);
+ }
+
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+set_login1_manager (IndicatorSessionActionsDbus * self,
+ Login1Manager * login1_manager)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_manager != NULL)
+ {
+ g_cancellable_cancel (p->login1_manager_cancellable);
+ g_clear_object (&p->login1_manager_cancellable);
+ g_clear_object (&p->login1_manager);
+ }
+
+ if (login1_manager != NULL)
+ {
+ p->login1_manager_cancellable = g_cancellable_new ();
+
+ p->login1_manager = g_object_ref (login1_manager);
+
+ login1_manager_call_can_suspend (p->login1_manager,
+ p->login1_manager_cancellable,
+ on_can_suspend_ready,
+ self);
+
+ login1_manager_call_can_hibernate (p->login1_manager,
+ p->login1_manager_cancellable,
+ on_can_hibernate_ready,
+ self);
+ }
+}
+
+static void
+on_session_manager_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+{
+ GError * err;
+ GnomeSessionManager * sm;
+
+ err = NULL;
+ sm = gnome_session_manager_proxy_new_for_bus_finish (res, &err);
+ if (err == NULL)
+ {
+ INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->session_manager = sm;
+ }
+
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+on_webcredentials_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+{
+ GError * err;
+ Webcredentials * webcredentials;
+
+ err = NULL;
+ webcredentials = webcredentials_proxy_new_for_bus_finish (res, &err);
+ if (err == NULL)
+ {
+ INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->webcredentials = webcredentials;
+
+ g_signal_connect_swapped (webcredentials, "notify::error-status",
+ G_CALLBACK(indicator_session_actions_notify_has_online_account_error), gself);
+
+ if (webcredentials_get_error_status (webcredentials))
+ indicator_session_actions_notify_has_online_account_error (gself);
+ }
+
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+on_end_session_dialog_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+{
+ GError * err;
+ EndSessionDialog * end_session_dialog;
+
+ err = NULL;
+ end_session_dialog = end_session_dialog_proxy_new_for_bus_finish (res, &err);
+ if (err == NULL)
+ {
+ INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->end_session_dialog = end_session_dialog;
+
+ indicator_session_actions_notify_can_prompt (INDICATOR_SESSION_ACTIONS(gself));
+ indicator_session_actions_notify_can_reboot (INDICATOR_SESSION_ACTIONS(gself));
+ }
+
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+/***
+**** Virtual Functions
+***/
+
+static gboolean
+my_can_lock (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ return !g_settings_get_boolean (p->lockdown_settings, "disable-lock-screen");
+}
+
+static gboolean
+my_can_logout (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ if (g_settings_get_boolean (p->indicator_settings, "suppress-logout-menuitem"))
+ return FALSE;
+
+ if (g_settings_get_boolean (p->lockdown_settings, "disable-log-out"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+my_can_reboot (IndicatorSessionActions * actions)
+{
+ IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS(actions);
+ priv_t * p = self->priv;
+
+ if (g_settings_get_boolean (p->indicator_settings, "suppress-restart-menuitem"))
+ return FALSE;
+
+ /* Shutdown and Restart are the same dialog prompt in Unity,
+ so disable the redundant 'Restart' menuitem in that mode */
+ if (!g_settings_get_boolean (p->indicator_settings, "suppress-shutdown-menuitem"))
+ if (get_prompt_status(self) == PROMPT_WITH_UNITY)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+my_can_switch (IndicatorSessionActions * self)
+{
+ const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ return p->seat_allows_activation
+ && !g_settings_get_boolean (p->lockdown_settings, "disable-user-switching");
+}
+
+static gboolean
+my_can_suspend (IndicatorSessionActions * self)
+{
+ const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ return p && p->can_suspend;
+}
+
+static gboolean
+my_can_hibernate (IndicatorSessionActions * self)
+{
+ const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ return p && p->can_hibernate;
+}
+
+static gboolean
+my_can_prompt (IndicatorSessionActions * self)
+{
+ return get_prompt_status(INDICATOR_SESSION_ACTIONS_DBUS(self)) != PROMPT_NONE;
+}
+
+static gboolean
+my_has_online_account_error (IndicatorSessionActions * self)
+{
+ const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ return p && (p->webcredentials) && (webcredentials_get_error_status (p->webcredentials));
+}
+
+static void
+my_suspend (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ g_return_if_fail (p->login1_manager != NULL);
+
+ login1_manager_call_suspend (p->login1_manager,
+ FALSE,
+ p->login1_manager_cancellable,
+ NULL,
+ NULL);
+}
+
+static void
+my_hibernate (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ g_return_if_fail (p->login1_manager != NULL);
+
+ login1_manager_call_hibernate (p->login1_manager,
+ FALSE,
+ p->login1_manager_cancellable,
+ NULL,
+ NULL);
+}
+
+/***
+**** End Session Dialog
+***/
+
+static void
+logout_now (IndicatorSessionActionsDbus * self)
+{
+ priv_t * p = self->priv;
+
+ g_return_if_fail (p->session_manager != NULL);
+
+ gnome_session_manager_call_logout (p->session_manager,
+ 1, /* don't prompt */
+ p->cancellable,
+ NULL,
+ NULL);
+}
+
+static void
+on_reboot_response (GObject * o,
+ GAsyncResult * res,
+ gpointer unused G_GNUC_UNUSED)
+{
+ GError * err = NULL;
+ login1_manager_call_reboot_finish (LOGIN1_MANAGER(o), res, &err);
+ if (err != NULL)
+ {
+ g_warning ("Unable to reboot: %s", err->message);
+ g_error_free (err);
+ }
+}
+
+static void
+reboot_now (IndicatorSessionActionsDbus * self)
+{
+ priv_t * p = self->priv;
+
+ g_return_if_fail (p->login1_manager != NULL);
+
+ login1_manager_call_reboot (p->login1_manager,
+ FALSE,
+ p->login1_manager_cancellable,
+ on_reboot_response,
+ NULL);
+}
+
+static void
+power_off_now (IndicatorSessionActionsDbus * self)
+{
+ priv_t * p = self->priv;
+
+ g_return_if_fail (p->login1_manager != NULL);
+
+ login1_manager_call_power_off (p->login1_manager,
+ FALSE,
+ p->login1_manager_cancellable,
+ NULL,
+ NULL);
+}
+
+static void
+stop_listening_to_dialog (IndicatorSessionActionsDbus * self)
+{
+ g_signal_handlers_disconnect_by_data (self->priv->end_session_dialog, self);
+}
+static void
+on_end_session_dialog_canceled (IndicatorSessionActionsDbus * self)
+{
+ stop_listening_to_dialog (self);
+}
+static void
+on_end_session_dialog_closed (IndicatorSessionActionsDbus * self)
+{
+ stop_listening_to_dialog (self);
+}
+
+static void
+on_open_end_session_dialog_ready (GObject * o,
+ GAsyncResult * res,
+ gpointer gself G_GNUC_UNUSED)
+{
+ GError * err = NULL;
+ end_session_dialog_call_open_finish (END_SESSION_DIALOG(o), res, &err);
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+show_unity_end_session_dialog (IndicatorSessionActionsDbus * self, int type)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+ gpointer o = p->end_session_dialog;
+ const char * inhibitor_paths[] = { NULL };
+
+ g_assert (o != NULL);
+
+ g_signal_connect_swapped (o, "confirmed-logout", G_CALLBACK(logout_now), self);
+ g_signal_connect_swapped (o, "confirmed-reboot", G_CALLBACK(reboot_now), self);
+ g_signal_connect_swapped (o, "confirmed-shutdown", G_CALLBACK(power_off_now), self);
+ g_signal_connect_swapped (o, "canceled", G_CALLBACK(on_end_session_dialog_canceled), self);
+ g_signal_connect_swapped (o, "closed", G_CALLBACK(on_end_session_dialog_closed), self);
+
+ end_session_dialog_call_open (p->end_session_dialog, type, 0, 0, inhibitor_paths,
+ p->cancellable,
+ on_open_end_session_dialog_ready,
+ self);
+}
+
+static gboolean
+zenity_question (IndicatorSessionActionsDbus * self,
+ const char * icon_name,
+ const char * title,
+ const char * text,
+ const char * ok_label,
+ const char * cancel_label)
+{
+ char * command_line;
+ int exit_status;
+ gboolean confirmed;
+
+ command_line = g_strdup_printf ("%s"
+ " --question"
+ " --icon-name=\"%s\""
+ " --title=\"%s\""
+ " --text=\"%s\""
+ " --ok-label=\"%s\""
+ " --cancel-label=\"%s\""
+ " --no-wrap",
+ self->priv->zenity,
+ icon_name,
+ title,
+ text,
+ ok_label,
+ cancel_label);
+
+ exit_status = -1;
+ if (!g_spawn_command_line_sync (command_line, NULL, NULL, &exit_status, NULL))
+ {
+ /* Treat failure-to-prompt as user confirmation.
+ Otherwise how will the user ever log out? */
+ confirmed = TRUE;
+ }
+ else
+ {
+ confirmed = exit_status == 0;
+ }
+
+ g_free (command_line);
+ return confirmed;
+}
+
+static void
+my_logout (IndicatorSessionActions * actions)
+{
+ IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions);
+
+ switch (get_prompt_status (self))
+ {
+ case PROMPT_WITH_UNITY:
+ show_unity_end_session_dialog (self, END_SESSION_TYPE_LOGOUT);
+ break;
+
+ case PROMPT_NONE:
+ logout_now (self);
+ break;
+
+ case PROMPT_WITH_ZENITY:
+ {
+ const char * primary = _("Are you sure you want to close all programs and log out?");
+ const char * secondary = _("Some software updates won't be applied until the computer next restarts.");
+ char * text = g_strdup_printf ("<big><b>%s</b></big>\n \n%s", primary, secondary);
+
+ gboolean confirmed = zenity_question (self,
+ "system-log-out",
+ _("Log Out"),
+ text,
+ _("Log Out"),
+ _("Cancel"));
+
+ g_free (text);
+
+ if (confirmed)
+ logout_now (self);
+ break;
+ }
+ }
+}
+
+static void
+my_reboot (IndicatorSessionActions * actions)
+{
+ IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions);
+
+ switch (get_prompt_status (self))
+ {
+ case PROMPT_WITH_UNITY:
+ show_unity_end_session_dialog (self, END_SESSION_TYPE_REBOOT);
+ break;
+
+ case PROMPT_NONE:
+ reboot_now (self);
+ break;
+
+ case PROMPT_WITH_ZENITY:
+ if (zenity_question (self,
+ "system-restart",
+ _("Restart"),
+ _("Are you sure you want to close all programs and restart the computer?"),
+ _("Restart"),
+ _("Cancel")))
+ reboot_now (self);
+ break;
+ }
+}
+
+static void
+my_power_off (IndicatorSessionActions * actions)
+{
+ IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions);
+
+ switch (get_prompt_status (self))
+ {
+ case PROMPT_WITH_UNITY:
+ /* NB: TYPE_REBOOT instead of TYPE_SHUTDOWN because
+ the latter adds lock & logout options in Unity... */
+ show_unity_end_session_dialog (self, END_SESSION_TYPE_REBOOT);
+ break;
+
+ case PROMPT_WITH_ZENITY:
+ if (zenity_question (self,
+ "system-shutdown",
+ _("Shut Down"),
+ _("Are you sure you want to close all programs and shut down the computer?"),
+ _("Shut Down"),
+ _("Cancel")))
+ power_off_now (self);
+ break;
+
+ case PROMPT_NONE:
+ power_off_now (self);
+ break;
+ }
+}
+
+/***
+****
+***/
+
+static void
+run_outside_app (const char * cmd)
+{
+ GError * err = NULL;
+ g_debug ("%s calling \"%s\"", G_STRFUNC, cmd);
+ g_spawn_command_line_async (cmd, &err);
+ log_and_clear_error (&err, G_STRLOC, G_STRFUNC);
+}
+
+static void
+my_help (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ run_outside_app ("yelp");
+}
+
+static void
+my_settings (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ run_outside_app ("gnome-control-center");
+}
+
+static void
+my_online_accounts (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ run_outside_app ("gnome-control-center credentials");
+}
+
+static void
+my_about (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ run_outside_app ("gnome-control-center info");
+}
+
+/***
+****
+***/
+
+static void
+my_switch_to_screensaver (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ g_return_if_fail (p->screen_saver != NULL);
+
+ gnome_screen_saver_call_lock (p->screen_saver, p->cancellable, NULL, NULL);
+}
+
+static void
+my_switch_to_greeter (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ g_return_if_fail (p->dm_seat != NULL);
+
+ display_manager_seat_call_switch_to_greeter (p->dm_seat,
+ p->dm_seat_cancellable,
+ NULL, NULL);
+}
+
+static void
+my_switch_to_guest (IndicatorSessionActions * self)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ g_return_if_fail (p->dm_seat != NULL);
+
+ display_manager_seat_call_switch_to_guest (p->dm_seat, "",
+ p->dm_seat_cancellable,
+ NULL, NULL);
+}
+
+static void
+my_switch_to_username (IndicatorSessionActions * self, const char * username)
+{
+ priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv;
+
+ g_return_if_fail (p->dm_seat != NULL);
+
+ display_manager_seat_call_switch_to_user (p->dm_seat, username, "",
+ p->dm_seat_cancellable,
+ NULL, NULL);
+}
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (o);
+ priv_t * p = self->priv;
+
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel (p->cancellable);
+ g_clear_object (&p->cancellable);
+ }
+
+ if (p->indicator_settings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->indicator_settings, self);
+ g_clear_object (&p->indicator_settings);
+ }
+
+ if (p->lockdown_settings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->lockdown_settings, self);
+ g_clear_object (&p->lockdown_settings);
+ }
+
+ if (p->webcredentials != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->webcredentials, self);
+ g_clear_object (&p->webcredentials);
+ }
+
+ if (p->end_session_dialog != NULL)
+ {
+ stop_listening_to_dialog (self);
+ g_clear_object (&p->end_session_dialog);
+ }
+
+ g_clear_object (&p->screen_saver);
+ g_clear_object (&p->session_manager);
+ set_dm_seat (self, NULL);
+ set_login1_manager (self, NULL);
+ set_login1_seat (self, NULL);
+
+ G_OBJECT_CLASS (indicator_session_actions_dbus_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (o);
+ priv_t * p = self->priv;
+
+ g_free (p->zenity);
+}
+
+
+/***
+**** GObject Boilerplate
+***/
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_actions_dbus_class_init (IndicatorSessionActionsDbusClass * klass)
+{
+ GObjectClass * object_class;
+ IndicatorSessionActionsClass * actions_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+
+ actions_class = INDICATOR_SESSION_ACTIONS_CLASS (klass);
+ actions_class->can_lock = my_can_lock;
+ actions_class->can_logout = my_can_logout;
+ actions_class->can_reboot = my_can_reboot;
+ actions_class->can_switch = my_can_switch;
+ actions_class->can_suspend = my_can_suspend;
+ actions_class->can_hibernate = my_can_hibernate;
+ actions_class->can_prompt = my_can_prompt;
+ actions_class->has_online_account_error = my_has_online_account_error;
+ actions_class->logout = my_logout;
+ actions_class->suspend = my_suspend;
+ actions_class->hibernate = my_hibernate;
+ actions_class->reboot = my_reboot;
+ actions_class->power_off = my_power_off;
+ actions_class->settings = my_settings;
+ actions_class->online_accounts = my_online_accounts;
+ actions_class->help = my_help;
+ actions_class->about = my_about;
+ actions_class->switch_to_screensaver = my_switch_to_screensaver;
+ actions_class->switch_to_greeter = my_switch_to_greeter;
+ actions_class->switch_to_guest = my_switch_to_guest;
+ actions_class->switch_to_username = my_switch_to_username;
+
+ g_type_class_add_private (klass, sizeof (IndicatorSessionActionsDbusPriv));
+}
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_actions_dbus_init (IndicatorSessionActionsDbus * self)
+{
+ priv_t * p;
+ GSettings * s;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_SESSION_ACTIONS_DBUS,
+ IndicatorSessionActionsDbusPriv);
+ p->cancellable = g_cancellable_new ();
+ p->seat_allows_activation = TRUE;
+ self->priv = p;
+
+ p->zenity = g_find_program_in_path ("zenity");
+
+ s = g_settings_new ("org.gnome.desktop.lockdown");
+ g_signal_connect_swapped (s, "changed::disable-lock-screen",
+ G_CALLBACK(indicator_session_actions_notify_can_lock), self);
+ g_signal_connect_swapped (s, "changed::disable-log-out",
+ G_CALLBACK(indicator_session_actions_notify_can_logout), self);
+ g_signal_connect_swapped (s, "changed::disable-user-switching",
+ G_CALLBACK(indicator_session_actions_notify_can_switch), self);
+ p->lockdown_settings = s;
+
+ s = g_settings_new ("com.canonical.indicator.session");
+ g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown",
+ G_CALLBACK(indicator_session_actions_notify_can_prompt), self);
+ g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown",
+ G_CALLBACK(indicator_session_actions_notify_can_reboot), self);
+ g_signal_connect_swapped (s, "changed::suppress-restart-menuitem",
+ G_CALLBACK(indicator_session_actions_notify_can_reboot), self);
+ g_signal_connect_swapped (s, "changed::suppress-shutdown-menuitem",
+ G_CALLBACK(indicator_session_actions_notify_can_reboot), self);
+ p->indicator_settings = s;
+
+ gnome_screen_saver_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gnome.ScreenSaver",
+ "/org/gnome/ScreenSaver",
+ p->cancellable,
+ on_screensaver_proxy_ready,
+ self);
+
+ gnome_session_manager_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gnome.SessionManager",
+ "/org/gnome/SessionManager",
+ p->cancellable,
+ on_session_manager_proxy_ready,
+ self);
+
+ webcredentials_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "com.canonical.indicators.webcredentials",
+ "/com/canonical/indicators/webcredentials",
+ p->cancellable,
+ on_webcredentials_proxy_ready,
+ self);
+
+ end_session_dialog_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "com.canonical.Unity",
+ "/org/gnome/SessionManager/EndSessionDialog",
+ p->cancellable,
+ on_end_session_dialog_proxy_ready,
+ self);
+}
+
+/***
+**** Public
+***/
+
+IndicatorSessionActions *
+indicator_session_actions_dbus_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_SESSION_ACTIONS_DBUS, NULL);
+
+ return INDICATOR_SESSION_ACTIONS (o);
+}
+
+void
+indicator_session_actions_dbus_set_proxies (IndicatorSessionActionsDbus * self,
+ Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * dm_seat)
+{
+ g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS_DBUS(self));
+
+ set_login1_manager (self, login1_manager);
+ set_login1_seat (self, login1_seat);
+ set_dm_seat (self, dm_seat);
+}
diff --git a/src/backend-dbus/actions.h b/src/backend-dbus/actions.h
new file mode 100644
index 0000000..d3d722d
--- /dev/null
+++ b/src/backend-dbus/actions.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INDICATOR_SESSION_ACTIONS_DBUS_H__
+#define __INDICATOR_SESSION_ACTIONS_DBUS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "../actions.h" /* parent class */
+#include "dbus-login1-manager.h"
+#include "dbus-login1-seat.h"
+#include "dbus-display-manager.h"
+
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_SESSION_ACTIONS_DBUS (indicator_session_actions_dbus_get_type())
+#define INDICATOR_SESSION_ACTIONS_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS, IndicatorSessionActionsDbus))
+#define INDICATOR_SESSION_ACTIONS_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS, IndicatorSessionActionsDbusClass))
+#define INDICATOR_IS_SESSION_ACTIONS_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS))
+
+typedef struct _IndicatorSessionActionsDbus IndicatorSessionActionsDbus;
+typedef struct _IndicatorSessionActionsDbusPriv IndicatorSessionActionsDbusPriv;
+typedef struct _IndicatorSessionActionsDbusClass IndicatorSessionActionsDbusClass;
+
+/**
+ * An implementation of IndicatorSessionActions that gets its user information
+ * from org.freedesktop.login1 org.freedesktop.DisplayManager over DBus.
+ */
+struct _IndicatorSessionActionsDbus
+{
+ /*< private >*/
+ IndicatorSessionActions parent;
+ IndicatorSessionActionsDbusPriv * priv;
+};
+
+struct _IndicatorSessionActionsDbusClass
+{
+ IndicatorSessionActionsClass parent_class;
+};
+
+GType indicator_session_actions_dbus_get_type (void);
+
+IndicatorSessionActions * indicator_session_actions_dbus_new (void);
+
+void indicator_session_actions_dbus_set_proxies (IndicatorSessionActionsDbus * self,
+ Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * dm_seat);
+
+
+G_END_DECLS
+
+#endif /* __INDICATOR_SESSION_ACTIONS_DBUS_H__ */
diff --git a/src/backend-dbus/backend-dbus.c b/src/backend-dbus/backend-dbus.c
new file mode 100644
index 0000000..547c6ab
--- /dev/null
+++ b/src/backend-dbus/backend-dbus.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "actions.h"
+#include "backend-dbus.h"
+#include "guest.h"
+#include "users.h"
+#include "utils.h"
+
+struct dbus_world_data
+{
+ GCancellable * cancellable;
+ IndicatorSessionActionsDbus * actions;
+ IndicatorSessionUsersDbus * users;
+ IndicatorSessionGuestDbus * guest;
+};
+
+static void
+on_proxies_ready (Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * display_manager_seat,
+ Accounts * account_manager,
+ GCancellable * cancellable,
+ gpointer gdata)
+{
+ struct dbus_world_data * data = gdata;
+
+ if (!g_cancellable_is_cancelled (cancellable))
+ {
+ if (data->actions != NULL)
+ indicator_session_actions_dbus_set_proxies (data->actions,
+ login1_manager,
+ login1_seat,
+ display_manager_seat);
+
+ if (data->users != NULL)
+ indicator_session_users_dbus_set_proxies (data->users,
+ login1_manager,
+ login1_seat,
+ display_manager_seat,
+ account_manager);
+
+ if (data->guest != NULL)
+ indicator_session_guest_dbus_set_proxies (data->guest,
+ login1_manager,
+ login1_seat,
+ display_manager_seat);
+ }
+
+ g_free (data);
+}
+
+/***
+****
+***/
+
+void
+backend_get (GCancellable * cancellable,
+ IndicatorSessionActions ** setme_actions,
+ IndicatorSessionUsers ** setme_users,
+ IndicatorSessionGuest ** setme_guest)
+{
+ struct dbus_world_data * data;
+
+ data = g_new0 (struct dbus_world_data, 1);
+
+ if (setme_actions != NULL)
+ {
+ IndicatorSessionActions * actions;
+ actions = indicator_session_actions_dbus_new ();
+ data->actions = INDICATOR_SESSION_ACTIONS_DBUS (actions);
+
+ *setme_actions = actions;
+ }
+
+ if (setme_users != NULL)
+ {
+ IndicatorSessionUsers * users;
+ users = indicator_session_users_dbus_new ();
+ data->users = INDICATOR_SESSION_USERS_DBUS (users);
+
+ *setme_users = users;
+ }
+
+ if (setme_guest != NULL)
+ {
+ IndicatorSessionGuest * guest;
+ guest = indicator_session_guest_dbus_new ();
+ data->guest = INDICATOR_SESSION_GUEST_DBUS (guest);
+
+ *setme_guest = guest;
+ }
+
+ data->cancellable = g_object_ref (cancellable);
+
+ indicator_session_util_get_session_proxies (on_proxies_ready,
+ data->cancellable,
+ data);
+}
diff --git a/src/backend-dbus/backend-dbus.h b/src/backend-dbus/backend-dbus.h
new file mode 100644
index 0000000..daf6ac3
--- /dev/null
+++ b/src/backend-dbus/backend-dbus.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INDICATOR_SESSION_BACKEND_DESKTOP_H__
+#define __INDICATOR_SESSION_BACKEND_DESKTOP_H__
+
+#include <gio/gio.h> /* GCancellable */
+
+#include "../actions.h"
+#include "../guest.h"
+#include "../users.h"
+
+G_BEGIN_DECLS
+
+void backend_get (GCancellable * cancellable,
+ IndicatorSessionActions ** setme_actions,
+ IndicatorSessionUsers ** setme_users,
+ IndicatorSessionGuest ** setme_guest);
+
+G_END_DECLS
+
+#endif
diff --git a/src/backend-dbus/com.canonical.indicators.webcredentials.xml b/src/backend-dbus/com.canonical.indicators.webcredentials.xml
new file mode 100644
index 0000000..d215081
--- /dev/null
+++ b/src/backend-dbus/com.canonical.indicators.webcredentials.xml
@@ -0,0 +1,82 @@
+<node>
+<!--
+ com.canonical.indicators.webcredentials:
+ @short_description: interface for handling login failures.
+
+ The service implementing this interface keeps track of login failures.
+ Failures are reported (usually by signon-ui) using the ReportFailure method,
+ are listed in the Failures property and can be removed by calling
+ RemoveFailures.
+
+ The ClearErrorStatus method can be called to clear the error indicator from
+ the system user menu.
+-->
+<interface name="com.canonical.indicators.webcredentials">
+ <!--
+ ReportFailure:
+ @account-id: the libaccounts ID of the account which failed to login.
+ @notification: dictionary of parameters for the OSD notification.
+
+ Inform the service about a failing account. The @account-id is added to the
+ list of the accounts in the Failures property, and a notification might be
+ displayed to the user.
+
+ The parameters currently recognized for the @notification argument are:
+ - DisplayName: string, description of the account (usually it's the
+ username)
+ -->
+ <method name="ReportFailure">
+ <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QVariantMap"/>
+ <arg name="account_id" type="u" direction="in"/>
+ <arg name="notification" type="a{sv}" direction="in"/>
+ </method>
+
+ <!--
+ RemoveFailures:
+ @account-ids: the libaccounts IDs of the accounts.
+
+ Remove the given account IDs from the list of the failed accounts.
+ -->
+ <method name="RemoveFailures">
+ <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QSet&lt;uint>"/>
+ <arg name="account_ids" type="au" direction="in"/>
+ </method>
+
+ <!--
+ ReauthenticateAccount:
+ @account-id: the libaccounts ID of the account.
+ @extra-parameters: dictionary of extra parameters (typically used to
+ specify a XWindowID).
+ @reauthenticated: %TRUE if the account could be reauthenticated and the
+ failure status has been cleared, %FALSE otherwise.
+
+ Tries to replay the failed authentications on the account. If all of them
+ succeed, then the account failure is cleared.
+ -->
+ <method name="ReauthenticateAccount">
+ <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QVariantMap"/>
+ <arg name="account_id" type="u" direction="in"/>
+ <arg name="extra_parameters" type="a{sv}" direction="in"/>
+ <arg name="reauthenticated" type="b" direction="out"/>
+ </method>
+
+ <!--
+ ClearErrorStatus:
+
+ Unsets the error indicator (if any) from the system user menu.
+ -->
+ <method name="ClearErrorStatus"/>
+
+ <!--
+ Failures: list of the libaccounts IDs of the failing accounts.
+ -->
+ <property name="Failures" type="au" access="read">
+ <annotation name="com.trolltech.QtDBus.QtTypeName" value="QSet&lt;uint>"/>
+ </property>
+
+ <!--
+ ErrorStatus: true if the indicator should display an error status.
+ -->
+ <property name="ErrorStatus" type="b" access="read"/>
+</interface>
+</node>
diff --git a/src/backend-dbus/guest.c b/src/backend-dbus/guest.c
new file mode 100644
index 0000000..3269097
--- /dev/null
+++ b/src/backend-dbus/guest.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include "guest.h"
+
+struct _IndicatorSessionGuestDbusPriv
+{
+ GCancellable * cancellable;
+
+ Login1Manager * login1_manager;
+ Login1Seat * login1_seat;
+ DisplayManagerSeat * dm_seat;
+
+ gboolean guest_is_active;
+ gboolean guest_is_logged_in;
+ gboolean guest_is_allowed;
+};
+
+typedef IndicatorSessionGuestDbusPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorSessionGuestDbus,
+ indicator_session_guest_dbus,
+ INDICATOR_TYPE_SESSION_GUEST)
+
+/***
+****
+***/
+
+static void
+set_guest_is_allowed_flag (IndicatorSessionGuestDbus * self, gboolean b)
+{
+ priv_t * p = self->priv;
+
+ if (p->guest_is_allowed != b)
+ {
+ p->guest_is_allowed = b;
+
+ indicator_session_guest_notify_allowed (INDICATOR_SESSION_GUEST (self));
+ }
+}
+
+static void
+set_guest_is_logged_in_flag (IndicatorSessionGuestDbus * self, gboolean b)
+{
+ priv_t * p = self->priv;
+
+ if (p->guest_is_logged_in != b)
+ {
+ p->guest_is_logged_in = b;
+
+ indicator_session_guest_notify_logged_in (INDICATOR_SESSION_GUEST (self));
+ }
+}
+
+static void
+set_guest_is_active_flag (IndicatorSessionGuestDbus * self, gboolean b)
+{
+ priv_t * p = self->priv;
+
+ if (p->guest_is_active != b)
+ {
+ p->guest_is_active = b;
+
+ indicator_session_guest_notify_active (INDICATOR_SESSION_GUEST(self));
+ }
+}
+
+/***
+****
+***/
+
+/* walk the sessions to see if guest is logged in or active */
+static void
+on_login1_manager_session_list_ready (GObject * o,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GVariant * sessions;
+ GError * err;
+
+ sessions = NULL;
+ err = NULL;
+ login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o),
+ &sessions,
+ res,
+ &err);
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s: %s", G_STRFUNC, err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ const gchar * const current_seat_id = g_getenv ("XDG_SEAT");
+ const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID");
+ gboolean is_logged_in = FALSE;
+ gboolean is_active = FALSE;
+ const gchar * session_id = NULL;
+ guint32 uid = 0;
+ const gchar * user_name = NULL;
+ const gchar * seat_id = NULL;
+ GVariantIter iter;
+
+ g_variant_iter_init (&iter, sessions);
+ while (g_variant_iter_loop (&iter, "(&su&s&so)", &session_id,
+ &uid,
+ &user_name,
+ &seat_id,
+ NULL))
+ {
+ gboolean is_current_session;
+ gboolean is_guest;
+
+ is_current_session = !g_strcmp0 (current_seat_id, seat_id)
+ && !g_strcmp0 (current_session_id, session_id);
+
+ is_guest = g_str_has_prefix (user_name, "guest-")
+ && (uid < 1000);
+
+ if (is_guest)
+ {
+ is_logged_in = TRUE;
+
+ is_active = is_current_session;
+ }
+ }
+
+ set_guest_is_logged_in_flag (gself, is_logged_in);
+ set_guest_is_active_flag (gself, is_active);
+
+ g_variant_unref (sessions);
+ }
+}
+
+static void
+update_session_list (IndicatorSessionGuestDbus * self)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_manager != NULL)
+ {
+ login1_manager_call_list_sessions (p->login1_manager,
+ p->cancellable,
+ on_login1_manager_session_list_ready,
+ self);
+ }
+}
+
+static void
+set_login1_manager (IndicatorSessionGuestDbus * self,
+ Login1Manager * login1_manager)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_manager != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_manager, self);
+
+ g_clear_object (&p->login1_manager);
+ }
+
+ if (login1_manager != NULL)
+ {
+ p->login1_manager = g_object_ref (login1_manager);
+
+ g_signal_connect_swapped (login1_manager, "session-new",
+ G_CALLBACK(update_session_list), self);
+ g_signal_connect_swapped (login1_manager, "session-removed",
+ G_CALLBACK(update_session_list), self);
+ update_session_list (self);
+ }
+}
+
+static void
+set_login1_seat (IndicatorSessionGuestDbus * self,
+ Login1Seat * login1_seat)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_seat != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_seat, self);
+ g_clear_object (&p->login1_seat);
+ }
+
+ if (login1_seat != NULL)
+ {
+ p->login1_seat = g_object_ref (login1_seat);
+
+ g_signal_connect_swapped (login1_seat, "notify::active-session",
+ G_CALLBACK(update_session_list), self);
+ update_session_list (self);
+ }
+}
+
+/***
+****
+***/
+
+static void
+on_switch_to_guest_done (GObject * o,
+ GAsyncResult * res,
+ gpointer unused G_GNUC_UNUSED)
+{
+ GError * err;
+
+ err = NULL;
+ display_manager_seat_call_switch_to_guest_finish (DISPLAY_MANAGER_SEAT(o),
+ res,
+ &err);
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message);
+
+ g_error_free (err);
+ }
+}
+
+static void
+my_switch_to_guest (IndicatorSessionGuest * guest)
+{
+ IndicatorSessionGuestDbus * self = INDICATOR_SESSION_GUEST_DBUS(guest);
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (self->priv->dm_seat != NULL);
+
+ display_manager_seat_call_switch_to_guest (self->priv->dm_seat,
+ "",
+ self->priv->cancellable,
+ on_switch_to_guest_done,
+ self);
+}
+
+static void
+on_notify_has_guest_account (DisplayManagerSeat * dm_seat,
+ GParamSpec * pspec G_GNUC_UNUSED,
+ IndicatorSessionGuestDbus * self)
+{
+ gboolean guest_exists = display_manager_seat_get_has_guest_account (dm_seat);
+ set_guest_is_allowed_flag (self, guest_exists);
+}
+
+static void
+set_display_manager_seat (IndicatorSessionGuestDbus * self,
+ DisplayManagerSeat * dm_seat)
+{
+ priv_t * p = self->priv;
+
+ if (p->dm_seat != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->dm_seat, self);
+
+ g_clear_object (&p->dm_seat);
+ }
+
+ if (dm_seat != NULL)
+ {
+ p->dm_seat = g_object_ref (dm_seat);
+
+ g_signal_connect (dm_seat, "notify::has-guest-account",
+ G_CALLBACK(on_notify_has_guest_account), self);
+ on_notify_has_guest_account (dm_seat, NULL, self);
+ }
+}
+
+/***
+**** IndiatorSessionGuest Virtual Functions
+***/
+
+static gboolean
+my_is_allowed (IndicatorSessionGuest * self)
+{
+ g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE);
+
+ return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_allowed;
+}
+
+static gboolean
+my_is_logged_in (IndicatorSessionGuest * self)
+{
+ g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE);
+
+ return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_logged_in;
+}
+
+static gboolean
+my_is_active (IndicatorSessionGuest * self)
+{
+ g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE);
+
+ return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_active;
+}
+
+/***
+**** GObject Virtual Functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorSessionGuestDbus * self = INDICATOR_SESSION_GUEST_DBUS (o);
+
+ if (self->priv->cancellable != NULL)
+ {
+ g_cancellable_cancel (self->priv->cancellable);
+ g_clear_object (&self->priv->cancellable);
+ }
+
+ set_login1_seat (self, NULL);
+ set_login1_manager (self, NULL);
+ set_display_manager_seat (self, NULL);
+
+ G_OBJECT_CLASS (indicator_session_guest_dbus_parent_class)->dispose (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_session_guest_dbus_class_init (IndicatorSessionGuestDbusClass * klass)
+{
+ GObjectClass * object_class;
+ IndicatorSessionGuestClass * guest_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = my_dispose;
+
+ guest_class = INDICATOR_SESSION_GUEST_CLASS (klass);
+ guest_class->is_allowed = my_is_allowed;
+ guest_class->is_logged_in = my_is_logged_in;
+ guest_class->is_active = my_is_active;
+ guest_class->switch_to_guest = my_switch_to_guest;
+
+ g_type_class_add_private (klass, sizeof (IndicatorSessionGuestDbusPriv));
+}
+
+static void
+indicator_session_guest_dbus_init (IndicatorSessionGuestDbus * self)
+{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_SESSION_GUEST_DBUS,
+ IndicatorSessionGuestDbusPriv);
+ p->cancellable = g_cancellable_new ();
+ self->priv = p;
+}
+
+/***
+**** Public
+***/
+
+IndicatorSessionGuest *
+indicator_session_guest_dbus_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_SESSION_GUEST_DBUS, NULL);
+
+ return INDICATOR_SESSION_GUEST (o);
+}
+
+void
+indicator_session_guest_dbus_set_proxies (IndicatorSessionGuestDbus * self,
+ Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * dm_seat)
+{
+ g_return_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self));
+
+ set_login1_manager (self, login1_manager);
+ set_login1_seat (self, login1_seat);
+ set_display_manager_seat (self, dm_seat);
+}
diff --git a/src/backend-dbus/guest.h b/src/backend-dbus/guest.h
new file mode 100644
index 0000000..73bb3ca
--- /dev/null
+++ b/src/backend-dbus/guest.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GUEST_DBUS_H__
+#define __GUEST_DBUS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "../guest.h" /* parent class */
+#include "dbus-login1-manager.h"
+#include "dbus-login1-seat.h"
+#include "dbus-display-manager.h"
+
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_SESSION_GUEST_DBUS (indicator_session_guest_dbus_get_type())
+#define INDICATOR_SESSION_GUEST_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS, IndicatorSessionGuestDbus))
+#define INDICATOR_SESSION_GUEST_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS, IndicatorSessionGuestDbusClass))
+#define INDICATOR_IS_SESSION_GUEST_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS))
+
+typedef struct _IndicatorSessionGuestDbus IndicatorSessionGuestDbus;
+typedef struct _IndicatorSessionGuestDbusPriv IndicatorSessionGuestDbusPriv;
+typedef struct _IndicatorSessionGuestDbusClass IndicatorSessionGuestDbusClass;
+
+/**
+ * An implementation of IndicatorSessionGuest that gets its user information
+ * from org.freedesktop.login1 and org.freedesktop.Accounts over DBus.
+ */
+struct _IndicatorSessionGuestDbus
+{
+ /*< private >*/
+ IndicatorSessionGuest parent;
+ IndicatorSessionGuestDbusPriv * priv;
+};
+
+struct _IndicatorSessionGuestDbusClass
+{
+ IndicatorSessionGuestClass parent_class;
+};
+
+GType indicator_session_guest_dbus_get_type (void);
+
+IndicatorSessionGuest * indicator_session_guest_dbus_new (void);
+
+void indicator_session_guest_dbus_set_proxies (IndicatorSessionGuestDbus * self,
+ Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * display_manager_seat);
+
+G_END_DECLS
+
+#endif
diff --git a/src/backend-dbus/org.freedesktop.Accounts.User.xml b/src/backend-dbus/org.freedesktop.Accounts.User.xml
new file mode 100644
index 0000000..53f54d4
--- /dev/null
+++ b/src/backend-dbus/org.freedesktop.Accounts.User.xml
@@ -0,0 +1,744 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" >
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <interface name="org.freedesktop.Accounts.User">
+
+ <method name="SetUserName">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new username.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users username. Note that it is usually not allowed
+ to have multiple users with the same username.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the username of any user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetRealName">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new name, typically in the form "Firstname Lastname".
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users real name.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own name</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the name of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetEmail">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="email" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new email address.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users email address.
+ </doc:para>
+ <doc:para>
+ Note that setting an email address in the AccountsService is
+ not the same as configuring a mail client. Mail clients might
+ default to email address that is configured here, though.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own email address</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the email address of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetLanguage">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="language" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new language, as a locale specification like "de_DE.UTF-8".
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users language.
+ </doc:para>
+ <doc:para>
+ The expectation is that display managers will start the
+ users session with this locale.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own language</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the language of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetXSession">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="user_set_x_session"/>
+ <arg name="x_session" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new xsession to start (e.g. "gnome")
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users x session.
+ </doc:para>
+ <doc:para>
+ The expectation is that display managers will log the user in to this
+ specified session, if available.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own language</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the language of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetLocation">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="location" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new location as a freeform string.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users location.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own location</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the location of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetHomeDirectory">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="homedir" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new homedir as an absolute path.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users home directory.
+ </doc:para>
+ <doc:para>
+ Note that changing the users home directory moves all the content
+ from the old location to the new one, and is potentially an
+ expensive operation.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the home directory of a user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetShell">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="shell" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new user shell.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users shell.
+ </doc:para>
+ <doc:para>
+ Note that setting the shell to a non-allowed program may
+ prevent the user from logging in.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the shell of a user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetIconFile">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="filename" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The absolute filename of a png file to use as the users icon.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users icon.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own icon</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the icon of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetLocked">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="locked" direction="in" type="b">
+ <doc:doc>
+ <doc:summary>
+ Whether to lock or unlock the users account.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Locks or unlocks a users account.
+ </doc:para>
+ <doc:para>
+ Locking an account prevents the user from logging in.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To lock or unlock user accounts</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetAccountType">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="accountType" direction="in" type="i">
+ <doc:doc>
+ <doc:summary>
+ The new account type, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Standard user</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Administrator</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Changes the users account type.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change an account type</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetPasswordMode">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="mode" direction="in" type="i">
+ <doc:doc>
+ <doc:summary>
+ The new password mode, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Regular password</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Password must be set at next login</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>No password</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Changes the users password mode.
+ </doc:para>
+ <doc:para>
+ Note that changing the password mode has the side-effect of
+ unlocking the account.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change a users password mode</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetPassword">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="password" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The crypted password.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <arg name="hint" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The password hint.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets a new password for this user.
+ </doc:para>
+ <doc:para>
+ Note that setting a password has the side-effect of
+ unlocking the account.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the password of a user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetAutomaticLogin">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="enabled" direction="in" type="b">
+ <doc:doc>
+ <doc:summary>
+ Whether to enable automatic login for this user.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Enables or disables automatic login for a user.
+ </doc:para>
+ <doc:para>
+ Note that usually only one user can have automatic login
+ enabled, so turning it on for a user will disable it for
+ the previously configured autologin user.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.set-login-option</doc:term>
+ <doc:definition>To change the login screen configuration</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <property name="Uid" type="t" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The uid of the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="UserName" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The username of the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="RealName" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users real name.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="AccountType" type="i" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users account type, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Standard user</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Administrator</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="HomeDirectory" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users home directory.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Shell" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users shell.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Email" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The email address.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Language" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users language, as a locale specification like "de_DE.UTF-8".
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="XSession" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users x session.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Location" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users location.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="LoginFrequency" type="t" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ How often the user has logged in.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="IconFile" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The filename of a png file containing the users icon.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Locked" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether the users account is locked.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="PasswordMode" type="i" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The password mode for the user account, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Regular password</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Password must be set at next login</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>No password</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="PasswordHint" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The password hint for the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="AutomaticLogin" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether automatic login is enabled for the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="SystemAccount" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether this is a 'system' account, like 'root' or 'nobody'.
+ System accounts should normally not appear in lists of
+ users, and ListCachedUsers will not include such accounts.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <signal name="Changed">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when the user is changed.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ </interface>
+</node>
diff --git a/src/backend-dbus/org.freedesktop.Accounts.xml b/src/backend-dbus/org.freedesktop.Accounts.xml
new file mode 100644
index 0000000..9c19761
--- /dev/null
+++ b/src/backend-dbus/org.freedesktop.Accounts.xml
@@ -0,0 +1,194 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" >
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <interface name="org.freedesktop.Accounts">
+
+ <!-- ************************************************************ -->
+
+ <method name="ListCachedUsers">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="users" direction="out" type="ao">
+ <doc:doc><doc:summary>Object paths of cached users</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Lists users which have logged into the system locally before.
+ This is not meant to return an exhaustive list of all users.
+ It is possible for <doc:ref type="method" to="Accounts.FindUserByName">FindUserByName()</doc:ref>
+ to return a user that's not on the list.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="FindUserById">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="id" direction="in" type="x">
+ <doc:doc><doc:summary>The uid to look up</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Finds a user by uid.
+ </doc:para>
+ </doc:description>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given uid exists</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="FindUserByName">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc><doc:summary>The username to look up</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Finds a user by its username.
+ </doc:para>
+ </doc:description>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given username exists</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="CreateUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc><doc:summary>The username for the new user</doc:summary></doc:doc>
+ </arg>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="fullname" direction="in" type="s">
+ <doc:doc><doc:summary>The real name for the new user</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of the new user</doc:summary></doc:doc>
+ </arg>
+ <arg name="accountType" direction="in" type="i">
+ <doc:doc>
+ <doc:summary>The account type, encoded as an integer</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Creates a new user account.
+ </doc:para>
+ <doc:para>
+ The accountType argument can take the following values:
+ </doc:para>
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Standard user</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Administrator</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>Supervised user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:description>
+ <doc:permission>
+ The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="DeleteUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="id" direction="in" type="x">
+ <doc:doc><doc:summary>The uid to delete</doc:summary></doc:doc>
+ </arg>
+ <arg name="removeFiles" direction="in" type="b">
+ <doc:doc><doc:summary>Whether to remove the users files</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Deletes a user account.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <signal name="UserAdded">
+ <arg name="user" type="o">
+ <doc:doc><doc:summary>Object path of the user that was added.</doc:summary></doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when a user is added.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <signal name="UserDeleted">
+ <arg name="user" type="o">
+ <doc:doc><doc:summary>Object path of the user that was deleted.</doc:summary></doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when a user is deleted.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <signal name="UserChanged">
+ <arg name="user" type="o">
+ <doc:doc><doc:summary>Object path of the user that was changed.</doc:summary></doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when a user is changed.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <property name="DaemonVersion" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The version of the running daemon.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ </interface>
+</node>
diff --git a/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml b/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml
new file mode 100644
index 0000000..07b5f29
--- /dev/null
+++ b/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml
@@ -0,0 +1,30 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="org.freedesktop.DisplayManager.Seat">
+
+ <property name="CanSwitch" type="b" access="read"/>
+
+ <property name="HasGuestAccount" type="b" access="read"/>
+
+ <property name="Sessions" type="ao" access="read"/>
+
+ <!-- Show greeter to allow new login / switch users -->
+ <method name="SwitchToGreeter"/>
+
+ <!-- Switch to a user, starting a new display if required -->
+ <method name="SwitchToUser">
+ <arg name="username" direction="in" type="s"/>
+ <arg name="session_name" direction="in" type="s"/>
+ </method>
+
+ <!-- Switch to the guest user -->
+ <method name="SwitchToGuest">
+ <arg name="session_name" direction="in" type="s"/>
+ </method>
+
+ <method name='Lock'/>
+
+ </interface>
+
+</node>
diff --git a/src/backend-dbus/org.freedesktop.login1.Manager.xml b/src/backend-dbus/org.freedesktop.login1.Manager.xml
new file mode 100644
index 0000000..91da5f2
--- /dev/null
+++ b/src/backend-dbus/org.freedesktop.login1.Manager.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0"?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.login1.Manager">
+ <method name="GetSession">
+ <arg name="id" type="s" direction="in"/>
+ <arg name="session" type="o" direction="out"/>
+ </method>
+ <method name="GetSessionByPID">
+ <arg name="pid" type="u" direction="in"/>
+ <arg name="session" type="o" direction="out"/>
+ </method>
+ <method name="GetUser">
+ <arg name="uid" type="u" direction="in"/>
+ <arg name="user" type="o" direction="out"/>
+ </method>
+ <method name="GetSeat">
+ <arg name="id" type="s" direction="in"/>
+ <arg name="seat" type="o" direction="out"/>
+ </method>
+ <method name="ListSessions">
+ <arg name="sessions" type="a(susso)" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="SessionObjectList"/>
+ </method>
+ <method name="ListUsers">
+ <arg name="users" type="a(uso)" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="UintStringPathList"/>
+ </method>
+ <method name="ListSeats">
+ <arg name="seats" type="a(so)" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="StringPathList"/>
+ </method>
+ <method name="CreateSession">
+ <arg name="uid" type="u" direction="in"/>
+ <arg name="leader" type="u" direction="in"/>
+ <arg name="sevice" type="s" direction="in"/>
+ <arg name="type" type="s" direction="in"/>
+ <arg name="klass" type="s" direction="in"/>
+ <arg name="seat" type="s" direction="in"/>
+ <arg name="vtnr" type="u" direction="in"/>
+ <arg name="tty" type="s" direction="in"/>
+ <arg name="display" type="s" direction="in"/>
+ <arg name="remote" type="b" direction="in"/>
+ <arg name="remote_user" type="s" direction="in"/>
+ <arg name="remote_host" type="s" direction="in"/>
+ <arg name="controllers" type="as" direction="in"/>
+ <arg name="reset_controllers" type="as" direction="in"/>
+ <arg name="kill_processes" type="b" direction="in"/>
+ <arg name="id" type="s" direction="out"/>
+ <arg name="path" type="o" direction="out"/>
+ <arg name="runtime_path" type="o" direction="out"/>
+ <arg name="fd" type="h" direction="out"/>
+ <arg name="seat" type="s" direction="out"/>
+ <arg name="vtnr" type="u" direction="out"/>
+ <arg name="existing" type="b" direction="out"/>
+ </method>
+ <method name="ReleaseSession">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ <method name="ActivateSession">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ <method name="ActivateSessionOnSeat">
+ <arg name="id" type="s" direction="in"/>
+ <arg name="seat" type="s" direction="in"/>
+ </method>
+ <method name="LockSession">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ <method name="UnlockSession">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ <method name="LockSessions"/>
+ <method name="KillSession">
+ <arg name="id" type="s" direction="in"/>
+ <arg name="who" type="s" direction="in"/>
+ <arg name="signal" type="s" direction="in"/>
+ </method>
+ <method name="KillUser">
+ <arg name="uid" type="u" direction="in"/>
+ <arg name="signal" type="s" direction="in"/>
+ </method>
+ <method name="TerminateSession">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ <method name="TerminateUser">
+ <arg name="uid" type="u" direction="in"/>
+ </method>
+ <method name="TerminateSeat">
+ <arg name="id" type="s" direction="in"/>
+ </method>
+ <method name="SetUserLinger">
+ <arg name="uid" type="u" direction="in"/>
+ <arg name="b" type="b" direction="in"/>
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="AttachDevice">
+ <arg name="seat" type="s" direction="in"/>
+ <arg name="sysfs" type="s" direction="in"/>
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="FlushDevices">
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="PowerOff">
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="Reboot">
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="Suspend">
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="Hibernate">
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="HybridSleep">
+ <arg name="interactive" type="b" direction="in"/>
+ </method>
+ <method name="CanPowerOff">
+ <arg name="result" type="s" direction="out"/>
+ </method>
+ <method name="CanReboot">
+ <arg name="result" type="s" direction="out"/>
+ </method>
+ <method name="CanSuspend">
+ <arg name="result" type="s" direction="out"/>
+ </method>
+ <method name="CanHibernate">
+ <arg name="result" type="s" direction="out"/>
+ </method>
+ <method name="CanHybridSleep">
+ <arg name="result" type="s" direction="out"/>
+ </method>
+ <method name="Inhibit">
+ <arg name="what" type="s" direction="in"/>
+ <arg name="who" type="s" direction="in"/>
+ <arg name="why" type="s" direction="in"/>
+ <arg name="mode" type="s" direction="in"/>
+ <arg name="fd" type="h" direction="out"/>
+ </method>
+ <method name="ListInhibitors">
+ <arg name="inhibitors" type="a(ssssuu)" direction="out"/>
+ <!-- TODO: Create an appropriate type -->
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/>
+ </method>
+ <signal name="SessionNew">
+ <arg name="id" type="s"/>
+ <arg name="path" type="o"/>
+ </signal>
+ <signal name="SessionRemoved">
+ <arg name="id" type="s"/>
+ <arg name="path" type="o"/>
+ </signal>
+ <signal name="UserNew">
+ <arg name="uid" type="u"/>
+ <arg name="path" type="o"/>
+ </signal>
+ <signal name="UserRemoved">
+ <arg name="uid" type="u"/>
+ <arg name="path" type="o"/>
+ </signal>
+ <signal name="SeatNew">
+ <arg name="id" type="s"/>
+ <arg name="path" type="o"/>
+ </signal>
+ <signal name="SeatRemoved">
+ <arg name="id" type="s"/>
+ <arg name="path" type="o"/>
+ </signal>
+ <signal name="PrepareForShutdown">
+ <arg name="active" type="b"/>
+ </signal>
+ <signal name="PrepareForSleep">
+ <arg name="active" type="b"/>
+ </signal>
+ <property name="ControlGroupHierarchy" type="s" access="read"/>
+ <property name="Controllers" type="as" access="read"/>
+ <property name="ResetControllers" type="as" access="read"/>
+ <property name="NAutoVTs" type="u" access="read"/>
+ <property name="KillOnlyUsers" type="as" access="read"/>
+ <property name="KillExcludeUsers" type="as" access="read"/>
+ <property name="KillUserProcesses" type="b" access="read"/>
+ <property name="IdleHint" type="b" access="read"/>
+ <property name="IdleSinceHint" type="t" access="read"/>
+ <property name="IdleSinceHintMonotonic" type="t" access="read"/>
+ <property name="BlockInhibited" type="s" access="read"/>
+ <property name="DelayInhibited" type="s" access="read"/>
+ <property name="InhibitDelayMaxUSec" type="t" access="read"/>
+ <property name="HandlePowerKey" type="s" access="read"/>
+ <property name="HandleSuspendKey" type="s" access="read"/>
+ <property name="HandleHibernateKey" type="s" access="read"/>
+ <property name="HandleLidSwitch" type="s" access="read"/>
+ <property name="IdleAction" type="s" access="read"/>
+ <property name="IdleActionUSec" type="t" access="read"/>
+ <property name="PreparingForShutdown" type="b" access="read"/>
+ <property name="PreparingForSleep" type="b" access="read"/>
+ </interface>
+</node>
diff --git a/src/backend-dbus/org.freedesktop.login1.Seat.xml b/src/backend-dbus/org.freedesktop.login1.Seat.xml
new file mode 100644
index 0000000..b73f724
--- /dev/null
+++ b/src/backend-dbus/org.freedesktop.login1.Seat.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.login1.Seat">
+ <method name="Terminate"/>
+ <method name="ActivateSession">
+ <arg name="id" direction="in" type="s"/>
+ </method>
+ <property name="Id" type="s" access="read"/>
+ <property name="ActiveSession" type="(so)" access="read"/>
+ <property name="CanMultiSession" type="b" access="read"/>
+ <property name="CanTTY" type="b" access="read"/>
+ <property name="CanGraphical" type="b" access="read"/>
+ <property name="Sessions" type="a(so)" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="StringPathList"/>
+ </property>
+ <property name="IdleHint" type="b" access="read"/>
+ <property name="IdleSinceHint" type="t" access="read"/>
+ <property name="IdleSinceHintMonotonic" type="t" access="read"/>
+ </interface>
+</node>
diff --git a/src/backend-dbus/org.freedesktop.login1.User.xml b/src/backend-dbus/org.freedesktop.login1.User.xml
new file mode 100644
index 0000000..8253706
--- /dev/null
+++ b/src/backend-dbus/org.freedesktop.login1.User.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.login1.User">
+ <method name="Terminate"/>
+ <method name="Kill">
+ <arg name="signal" direction="in" type="s"/>
+ </method>
+ <property name="UID" type="u" access="read"/>
+ <property name="GID" type="u" access="read"/>
+ <property name="Name" type="s" access="read"/>
+ <property name="Timestamp" type="t" access="read"/>
+ <property name="TimestampMonotonic" type="t" access="read"/>
+ <property name="RuntimePath" type="s" access="read"/>
+ <property name="DefaultControlGroup" type="s" access="read"/>
+ <property name="Service" type="s" access="read"/>
+ <property name="Display" type="(so)" access="read"/>
+ <property name="State" type="s" access="read"/>
+ <property name="Sessions" type="a(so)" access="read"/>
+ <property name="IdleHint" type="b" access="read"/>
+ <property name="IdleSinceHint" type="t" access="read"/>
+ <property name="IdleSinceHintMonotonic" type="t" access="read"/>
+ </interface>
+ <interface name="org.freedesktop.DBus.Properties">
+ <method name="Get">
+ <arg name="interface" direction="in" type="s"/>
+ <arg name="property" direction="in" type="s"/>
+ <arg name="value" direction="out" type="v"/>
+ </method>
+ <method name="GetAll">
+ <arg name="interface" direction="in" type="s"/>
+ <arg name="properties" direction="out" type="a{sv}"/>
+ </method>
+ <method name="Set">
+ <arg name="interface" direction="in" type="s"/>
+ <arg name="property" direction="in" type="s"/>
+ <arg name="value" direction="in" type="v"/>
+ </method>
+ <signal name="PropertiesChanged">
+ <arg type="s" name="interface"/>
+ <arg type="a{sv}" name="changed_properties"/>
+ <arg type="as" name="invalidated_properties"/>
+ </signal>
+ </interface>
+<interface name="org.freedesktop.DBus.Peer">
+ <method name="Ping"/>
+ <method name="GetMachineId">
+ <arg type="s" name="machine_uuid" direction="out"/>
+ </method>
+</interface>
+ <interface name="org.freedesktop.DBus.Introspectable">
+ <method name="Introspect">
+ <arg name="data" type="s" direction="out"/>
+ </method>
+ </interface>
+</node>
diff --git a/src/backend-dbus/org.gnome.ScreenSaver.xml b/src/backend-dbus/org.gnome.ScreenSaver.xml
new file mode 100644
index 0000000..c21fdc5
--- /dev/null
+++ b/src/backend-dbus/org.gnome.ScreenSaver.xml
@@ -0,0 +1,16 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <interface name="org.gnome.ScreenSaver">
+
+ <method name="Lock">
+ </method>
+
+ <method name="SimulateUserActivity">
+ </method>
+
+ </interface>
+
+</node>
diff --git a/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml b/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml
new file mode 100644
index 0000000..5392de0
--- /dev/null
+++ b/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <interface name="org.gnome.SessionManager.EndSessionDialog">
+ <method name="Open">
+ <arg type="u" name="type" direction="in">
+ <doc:doc>
+ <doc:summary>
+ The type of dialog to show.
+ 0 for logout, 1 for shutdown, 2 for restart.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="u" name="timestamp" direction="in">
+ <doc:doc>
+ <doc:summary>
+ Timestamp of the user-initiated event which triggered
+ the call, or 0 if the call was not triggered by an event.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="u" name="seconds_to_stay_open" direction="in">
+ <doc:doc>
+ <doc:summary>
+ The number of seconds which the dialog should stay open
+ before automatic action is taken.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="ao" name="inhibitor_object_paths" direction="in">
+ <doc:doc>
+ <doc:summary>
+ The object paths of all inhibitors that are registered
+ for the action.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:summary>
+ This function opens a dialog which asks the user for confirmation
+ of a logout, poweroff or reboot action. The dialog has a timeout
+ after which the action is automatically taken, and it should show
+ the inhibitors to the user.
+ </doc:summary>
+ </doc:doc>
+ </method>
+ <signal name="ConfirmedLogout" />
+ <signal name="ConfirmedReboot" />
+ <signal name="ConfirmedShutdown" />
+ <signal name="Canceled" />
+ <signal name="Closed" />
+ </interface>
+</node>
diff --git a/src/backend-dbus/org.gnome.SessionManager.xml b/src/backend-dbus/org.gnome.SessionManager.xml
new file mode 100644
index 0000000..eb69180
--- /dev/null
+++ b/src/backend-dbus/org.gnome.SessionManager.xml
@@ -0,0 +1,451 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <interface name="org.gnome.SessionManager">
+
+ <!-- Initialization phase interfaces -->
+
+ <method name="Setenv">
+ <arg name="variable" type="s" direction="in">
+ <doc:doc>
+ <doc:summary>The variable name</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg name="value" type="s" direction="in">
+ <doc:doc>
+ <doc:summary>The value</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Adds the variable name to the application launch environment with the specified value. May only be used during the Session Manager initialization phase.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="GetLocale">
+ <arg name="category" type="i" direction="in">
+ <doc:doc>
+ <doc:summary>The locale category</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg name="value" type="s" direction="out">
+ <doc:doc>
+ <doc:summary>The value</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Reads the current state of the specific locale category.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="InitializationError">
+ <arg name="message" type="s" direction="in">
+ <doc:doc>
+ <doc:summary>The error message</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg name="fatal" type="b" direction="in">
+ <doc:doc>
+ <doc:summary>Whether the error should be treated as fatal</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>May be used by applications launched during the Session Manager initialization phase to indicate there was a problem.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <!-- Running phase interfaces -->
+
+ <method name="RegisterClient">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg type="s" name="app_id" direction="in">
+ <doc:doc>
+ <doc:summary>The application identifier</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="s" name="client_startup_id" direction="in">
+ <doc:doc>
+ <doc:summary>Client startup identifier</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="o" name="client_id" direction="out">
+ <doc:doc>
+ <doc:summary>The object path of the newly registered client</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Register the caller as a Session Management client.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="UnregisterClient">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg type="o" name="client_id" direction="in">
+ <doc:doc>
+ <doc:summary>The object path of the client</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Unregister the specified client from Session Management.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="Inhibit">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg type="s" name="app_id" direction="in">
+ <doc:doc>
+ <doc:summary>The application identifier</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="u" name="toplevel_xid" direction="in">
+ <doc:doc>
+ <doc:summary>The toplevel X window identifier</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="s" name="reason" direction="in">
+ <doc:doc>
+ <doc:summary>The reason for the inhibit</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="u" name="flags" direction="in">
+ <doc:doc>
+ <doc:summary>Flags that specify what should be inhibited</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="u" name="inhibit_cookie" direction="out">
+ <doc:doc>
+ <doc:summary>The cookie</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:summary>
+ Proactively indicates that the calling application is performing an action that should not be interrupted and sets a reason to be displayed to the user when an interruption is about to take placea.
+ </doc:summary>
+ <doc:description>
+ <doc:para>Applications should invoke this method when they begin an operation that
+ should not be interrupted, such as creating a CD or DVD. The types of actions
+ that may be blocked are specified by the flags parameter. When the application
+ completes the operation it should call <doc:ref type="method" to="org.gnome.SessionManager.Uninhibit">Uninhibit()</doc:ref>
+ or disconnect from the session bus.
+ </doc:para>
+ <doc:para>
+ Applications should not expect that they will always be able to block the
+ action. In most cases, users will be given the option to force the action
+ to take place.
+ </doc:para>
+ <doc:para>
+ Reasons should be short and to the point.
+ </doc:para>
+ <doc:para>
+ The flags parameter must include at least one of the following:
+ <doc:list>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Inhibit logging out</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>Inhibit user switching</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>4</doc:term>
+ <doc:definition>Inhibit suspending the session or computer</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>8</doc:term>
+ <doc:definition>Inhibit the session being marked as idle</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>16</doc:term>
+ <doc:definition>Inhibit auto-mounting removable media for the session</doc:definition>
+ </doc:item>
+ </doc:list>
+ Values for flags may be bitwise or'ed together.
+ </doc:para>
+ <doc:para>
+ The returned cookie is used to uniquely identify this request. It should be used
+ as an argument to <doc:ref type="method" to="org.gnome.SessionManager.Uninhibit">Uninhibit()</doc:ref> in
+ order to remove the request.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="Uninhibit">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg type="u" name="inhibit_cookie" direction="in">
+ <doc:doc>
+ <doc:summary>The cookie</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Cancel a previous call to <doc:ref type="method" to="org.gnome.SessionManager.Inhibit">Inhibit()</doc:ref> identified by the cookie.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="IsInhibited">
+ <arg type="u" name="flags" direction="in">
+ <doc:doc>
+ <doc:summary>Flags that spefify what should be inhibited</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg type="b" name="is_inhibited" direction="out">
+ <doc:doc>
+ <doc:summary>Returns TRUE if any of the operations in the bitfield flags are inhibited</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Determine if operation(s) specified by the flags
+ are currently inhibited. Flags are same as those accepted
+ by the
+ <doc:ref type="method" to="org.gnome.SessionManager.Inhibit">Inhibit()</doc:ref>
+ method.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="GetClients">
+ <arg name="clients" direction="out" type="ao">
+ <doc:doc>
+ <doc:summary>an array of client IDs</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>This gets a list of all the <doc:ref type="interface" to="org.gnome.SessionManager.Client">Clients</doc:ref>
+ that are currently known to the session manager.</doc:para>
+ <doc:para>Each Client ID is an D-Bus object path for the object that implements the
+ <doc:ref type="interface" to="org.gnome.SessionManager.Client">Client</doc:ref> interface.</doc:para>
+ </doc:description>
+ <doc:seealso><doc:ref type="interface" to="org.gnome.SessionManager.Client">org.gnome.SessionManager.Client</doc:ref></doc:seealso>
+ </doc:doc>
+ </method>
+
+ <method name="GetInhibitors">
+ <arg name="inhibitors" direction="out" type="ao">
+ <doc:doc>
+ <doc:summary>an array of inhibitor IDs</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>This gets a list of all the <doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">Inhibitors</doc:ref>
+ that are currently known to the session manager.</doc:para>
+ <doc:para>Each Inhibitor ID is an D-Bus object path for the object that implements the
+ <doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">Inhibitor</doc:ref> interface.</doc:para>
+ </doc:description>
+ <doc:seealso><doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">org.gnome.SessionManager.Inhibitor</doc:ref></doc:seealso>
+ </doc:doc>
+ </method>
+
+
+ <method name="IsAutostartConditionHandled">
+ <arg name="condition" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>The autostart condition string</doc:summary>
+ </doc:doc>
+ </arg>
+ <arg name="handled" direction="out" type="b">
+ <doc:doc>
+ <doc:summary>True if condition is handled, false otherwise</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Allows the caller to determine whether the session manager is
+ handling changes to the specified autostart condition.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="Shutdown">
+ <doc:doc>
+ <doc:description>
+ <doc:para>Request a shutdown dialog.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="Reboot">
+ <doc:doc>
+ <doc:description>
+ <doc:para>Request a reboot dialog.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="CanShutdown">
+ <arg name="is_available" direction="out" type="b">
+ <doc:doc>
+ <doc:summary>True if shutdown is available to the user, false otherwise</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Allows the caller to determine whether or not it's okay to show
+ a shutdown option in the UI</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="Logout">
+ <arg name="mode" type="u" direction="in">
+ <doc:doc>
+ <doc:summary>The type of logout that is being requested</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Request a logout dialog</doc:para>
+ <doc:para>
+ Allowed values for the mode parameter are:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Normal.</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>No confirmation inferface should be shown.</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>Forcefully logout. No confirmation will be shown and any inhibitors will be ignored.</doc:definition>
+ </doc:item>
+ </doc:list>
+ Values for flags may be bitwise or'ed together.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="IsSessionRunning">
+ <arg name="running" direction="out" type="b">
+ <doc:doc>
+ <doc:summary>True if the session has entered the Running phase, false otherwise</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Allows the caller to determine whether the session manager
+ has entered the Running phase, in case the client missed the
+ SessionRunning signal.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <!-- Signals -->
+
+ <signal name="ClientAdded">
+ <arg name="id" type="o">
+ <doc:doc>
+ <doc:summary>The object path for the added client</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Emitted when a client has been added to the session manager.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+ <signal name="ClientRemoved">
+ <arg name="id" type="o">
+ <doc:doc>
+ <doc:summary>The object path for the removed client</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Emitted when a client has been removed from the session manager.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <signal name="InhibitorAdded">
+ <arg name="id" type="o">
+ <doc:doc>
+ <doc:summary>The object path for the added inhibitor</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Emitted when an inhibitor has been added to the session manager.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+ <signal name="InhibitorRemoved">
+ <arg name="id" type="o">
+ <doc:doc>
+ <doc:summary>The object path for the removed inhibitor</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>Emitted when an inhibitor has been removed from the session manager.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <signal name="SessionRunning">
+ <doc:doc>
+ <doc:description>
+ <doc:para>Indicates the session has entered the Running phase.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <signal name="SessionOver">
+ <doc:doc>
+ <doc:description>
+ <doc:para>Indicates the session is about to end.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <!-- Properties -->
+
+ <property name="SessionName" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>The name of the session that has been loaded.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="SessionIsActive" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>If true, the session is currently in the
+ foreground and available for user input.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="InhibitedActions" type="u" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>A bitmask of flags to indicate which actions
+ are inhibited. See the Inhibit() function's description
+ for a list of possible values.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ </interface>
+</node>
diff --git a/src/backend-dbus/users.c b/src/backend-dbus/users.c
new file mode 100644
index 0000000..f770695
--- /dev/null
+++ b/src/backend-dbus/users.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dbus-user.h"
+
+#include "users.h"
+
+struct _IndicatorSessionUsersDbusPriv
+{
+ Login1Manager * login1_manager;
+ Login1Seat * login1_seat;
+ DisplayManagerSeat * dm_seat;
+ Accounts * accounts;
+
+ /* hash table of int uids to UserRecord* */
+ GHashTable * uid_to_account;
+
+ /* a hashset of int uids of users who are logged in */
+ GHashTable * logins;
+
+ /* the user-id of the owner of the active session */
+ guint active_uid;
+
+ /* true if this is a live session */
+ gboolean is_live;
+
+ GCancellable * cancellable;
+
+ guint update_list_tag;
+};
+
+typedef IndicatorSessionUsersDbusPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorSessionUsersDbus,
+ indicator_session_users_dbus,
+ INDICATOR_TYPE_SESSION_USERS)
+
+/***
+****
+***/
+
+struct UserRecord
+{
+ AccountsUser * user;
+
+ gulong signal_id;
+};
+
+struct UserRecord *
+user_record_new (AccountsUser * user, gulong signal_id)
+{
+ struct UserRecord * rec;
+ rec = g_new (struct UserRecord, 1);
+ rec->user = g_object_ref (user);
+ rec->signal_id = signal_id;
+ return rec;
+}
+
+static void
+user_record_free (struct UserRecord * rec)
+{
+ g_signal_handler_disconnect (rec->user, rec->signal_id);
+ g_object_unref (G_OBJECT (rec->user));
+ g_free (rec);
+}
+
+/***
+****
+***/
+
+/* get our private org.freedesktop.Accounts.User proxy for the given uid */
+static AccountsUser *
+get_user_for_uid (IndicatorSessionUsersDbus * self, guint uid)
+{
+ struct UserRecord * rec;
+
+ if ((rec = g_hash_table_lookup (self->priv->uid_to_account,
+ GUINT_TO_POINTER(uid))))
+ return rec->user;
+
+ return NULL;
+}
+
+static gboolean
+is_tracked_uid (IndicatorSessionUsersDbus * self, guint uid)
+{
+ return get_user_for_uid (self, uid) != NULL;
+}
+
+static void
+emit_user_added (IndicatorSessionUsersDbus * self, guint32 uid)
+{
+ if (is_tracked_uid (self, uid))
+ indicator_session_users_added (INDICATOR_SESSION_USERS(self), uid);
+}
+
+static void
+emit_user_changed (IndicatorSessionUsersDbus * self, guint32 uid)
+{
+ if (is_tracked_uid (self, uid))
+ indicator_session_users_changed (INDICATOR_SESSION_USERS(self), uid);
+}
+
+static void
+emit_user_removed (IndicatorSessionUsersDbus * self, guint32 uid)
+{
+ indicator_session_users_removed (INDICATOR_SESSION_USERS(self), uid);
+}
+
+/***
+****
+***/
+
+static void
+set_is_live_session_flag (IndicatorSessionUsersDbus * self, gboolean b)
+{
+ priv_t * p = self->priv;
+
+ if (p->is_live != b)
+ {
+ p->is_live = b;
+
+ indicator_session_users_notify_is_live_session (INDICATOR_SESSION_USERS (self));
+ }
+}
+
+static void
+set_active_uid (IndicatorSessionUsersDbus * self, guint uid)
+{
+ priv_t * p = self->priv;
+
+ if (p->active_uid != uid)
+ {
+ const guint old_uid = p->active_uid;
+
+ p->active_uid = uid;
+
+ emit_user_changed (self, old_uid);
+ emit_user_changed (self, uid);
+ }
+}
+
+static void
+set_logins (IndicatorSessionUsersDbus * self, GHashTable * logins)
+{
+ GHashTable * old_logins = self->priv->logins;
+ gpointer uid;
+ GHashTableIter iter;
+
+ self->priv->logins = g_hash_table_ref (logins);
+
+ /* fire 'user changed' event for users who logged out */
+ g_hash_table_iter_init (&iter, old_logins);
+ while ((g_hash_table_iter_next (&iter, &uid, NULL)))
+ if (!g_hash_table_contains (logins, uid))
+ emit_user_changed (self, GPOINTER_TO_UINT(uid));
+
+ /* fire 'user changed' event for users who logged in */
+ g_hash_table_iter_init (&iter, logins);
+ while ((g_hash_table_iter_next (&iter, &uid, NULL)))
+ if (!g_hash_table_contains (old_logins, uid))
+ emit_user_changed (self, GPOINTER_TO_UINT(uid));
+
+ g_hash_table_destroy (old_logins);
+}
+
+/***
+**** User Account Tracking
+***/
+
+static void create_user_proxy_for_path (IndicatorSessionUsersDbus *, const char *);
+
+/* called when a user proxy gets the 'Changed' signal */
+static void
+on_user_changed (AccountsUser * user, gpointer gself)
+{
+ /* Accounts.User doesn't update properties in the standard way,
+ * so create a new proxy to pull in the new properties.
+ * The older proxy is freed when it's replaced in our accounts hash */
+ const char * path = g_dbus_proxy_get_object_path (G_DBUS_PROXY(user));
+ create_user_proxy_for_path (gself, path);
+}
+
+static void
+track_user (IndicatorSessionUsersDbus * self,
+ AccountsUser * user)
+{
+ const guint32 uid = accounts_user_get_uid (user);
+ priv_t * p = self->priv;
+ gulong id;
+ const gboolean already_had_user = is_tracked_uid (self, uid);
+
+ id = g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self);
+ g_hash_table_insert (p->uid_to_account,
+ GUINT_TO_POINTER (uid),
+ user_record_new (user, id));
+
+ if (already_had_user)
+ emit_user_changed (self, uid);
+ else
+ emit_user_added (self, uid);
+}
+
+static void
+untrack_user (IndicatorSessionUsersDbus * self,
+ const gchar * path)
+{
+ guint uid;
+ gpointer key;
+ gpointer val;
+ GHashTableIter iter;
+ priv_t * p = self->priv;
+
+ /* find the uid matching this object path */
+ uid = 0;
+ g_hash_table_iter_init (&iter, p->uid_to_account);
+ while (!uid && g_hash_table_iter_next (&iter, &key, &val))
+ {
+ struct UserRecord * rec = val;
+ GDBusProxy * proxy = G_DBUS_PROXY (rec->user);
+ if (!g_strcmp0 (path, g_dbus_proxy_get_object_path (proxy)))
+ uid = GPOINTER_TO_UINT (key);
+ }
+
+ if (uid)
+ {
+ g_hash_table_remove (p->uid_to_account, GUINT_TO_POINTER(uid));
+
+ emit_user_removed (self, uid);
+ }
+}
+
+/* We got a new org.freedesktop.Accounts.User proxy.
+ If it's one we want to remember, pass it to track_user() */
+static void
+on_user_proxy_ready (GObject * o G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer self)
+{
+ GError * err;
+ AccountsUser * user;
+
+ err = NULL;
+ user = accounts_user_proxy_new_for_bus_finish (res, &err);
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s: %s", G_STRFUNC, err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ if (!accounts_user_get_system_account (user))
+ track_user (self, user);
+
+ g_object_unref (user);
+ }
+}
+
+static void
+create_user_proxy_for_path (IndicatorSessionUsersDbus * self,
+ const char * path)
+{
+ accounts_user_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+ "org.freedesktop.Accounts",
+ path,
+ self->priv->cancellable,
+ on_user_proxy_ready, self);
+}
+
+/* create proxy objects for everything in Account's user-list */
+static void
+on_user_list_ready (GObject * o, GAsyncResult * res, gpointer gself)
+{
+ GError * err;
+ gchar ** paths;
+
+ err = NULL;
+ paths = NULL;
+ accounts_call_list_cached_users_finish (ACCOUNTS(o), &paths, res, &err);
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ int i;
+
+ for (i=0; paths && paths[i]; ++i)
+ create_user_proxy_for_path (gself, paths[i]);
+
+ g_strfreev (paths);
+ }
+}
+
+static void
+set_account_manager (IndicatorSessionUsersDbus * self, Accounts * a)
+{
+ priv_t * p = self->priv;
+
+ if (p->accounts != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->accounts, self);
+ g_clear_object (&p->accounts);
+ }
+
+ if (a != NULL)
+ {
+ p->accounts = g_object_ref (a);
+
+ accounts_call_list_cached_users (a,
+ self->priv->cancellable,
+ on_user_list_ready,
+ self);
+
+ g_signal_connect_swapped (a, "user-added",
+ G_CALLBACK(create_user_proxy_for_path), self);
+
+ g_signal_connect_swapped (a, "user-deleted",
+ G_CALLBACK(untrack_user), self);
+ }
+}
+
+/***
+****
+***/
+
+/* Based on the login1 manager's list of current sessions,
+ update our 'logins', 'is_live', and 'active_uid' fields */
+static void
+on_login1_manager_session_list_ready (GObject * o,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GVariant * sessions;
+ GError * err;
+
+ sessions = NULL;
+ err = NULL;
+ login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o),
+ &sessions,
+ res,
+ &err);
+
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s: %s", G_STRFUNC, err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ const gchar * const current_seat_id = g_getenv ("XDG_SEAT");
+ const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID");
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself);
+ const gchar * session_id = NULL;
+ guint32 uid = 0;
+ const gchar * user_name = NULL;
+ const gchar * seat_id = NULL;
+ const gchar * path = NULL;
+ gboolean is_live_session = FALSE;
+ GHashTable * logins = g_hash_table_new (g_direct_hash, g_direct_equal);
+ GVariantIter iter;
+
+ g_variant_iter_init (&iter, sessions);
+ while (g_variant_iter_loop (&iter, "(&su&s&s&o)", &session_id,
+ &uid,
+ &user_name,
+ &seat_id,
+ &path))
+ {
+ /* only track sessions on our seat */
+ if (g_strcmp0 (seat_id, current_seat_id))
+ continue;
+
+ if (!g_strcmp0 (session_id, current_session_id))
+ {
+ set_active_uid (self, uid);
+
+ if ((uid==999) && !g_strcmp0 (user_name, "ubuntu"))
+ is_live_session = TRUE;
+ }
+
+ g_hash_table_add (logins, GINT_TO_POINTER(uid));
+ }
+
+ set_is_live_session_flag (self, is_live_session);
+ set_logins (self, logins);
+
+ g_hash_table_unref (logins);
+ g_variant_unref (sessions);
+ }
+}
+
+static void
+update_session_list (IndicatorSessionUsersDbus * self)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_manager != NULL)
+ {
+ login1_manager_call_list_sessions (p->login1_manager,
+ p->cancellable,
+ on_login1_manager_session_list_ready,
+ self);
+ }
+}
+
+static gboolean
+on_update_session_list_timer (gpointer gself)
+{
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself);
+
+ update_session_list (self);
+
+ self->priv->update_list_tag = 0;
+ return G_SOURCE_REMOVE;
+}
+
+/* A dead session can still show up in list-sessions for a few seconds.
+ So just to be safe, queue up a rebuild for a few seconds from now */
+static void
+update_session_list_twice (IndicatorSessionUsersDbus * self)
+{
+ priv_t * p = self->priv;
+
+ update_session_list (self);
+
+ if (p->update_list_tag == 0)
+ p->update_list_tag = g_timeout_add_seconds (5,
+ on_update_session_list_timer,
+ self);
+}
+
+static void
+set_login1_manager (IndicatorSessionUsersDbus * self,
+ Login1Manager * login1_manager)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_manager != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_manager, self);
+
+ g_clear_object (&p->login1_manager);
+ }
+
+ if (login1_manager != NULL)
+ {
+ p->login1_manager = g_object_ref (login1_manager);
+
+ g_signal_connect_swapped (login1_manager, "session-new",
+ G_CALLBACK(update_session_list), self);
+ g_signal_connect_swapped (login1_manager, "session-removed",
+ G_CALLBACK(update_session_list_twice), self);
+ g_signal_connect_swapped (login1_manager, "user-new",
+ G_CALLBACK(update_session_list), self);
+ g_signal_connect_swapped (login1_manager, "user-removed",
+ G_CALLBACK(update_session_list_twice), self);
+ update_session_list (self);
+ }
+}
+
+static void
+set_login1_seat (IndicatorSessionUsersDbus * self,
+ Login1Seat * login1_seat)
+{
+ priv_t * p = self->priv;
+
+ if (p->login1_seat != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_seat, self);
+
+ g_clear_object (&p->login1_seat);
+ }
+
+ if (login1_seat != NULL)
+ {
+ p->login1_seat = g_object_ref (login1_seat);
+
+ g_signal_connect_swapped (login1_seat, "notify::active-session",
+ G_CALLBACK(update_session_list), self);
+ update_session_list (self);
+ }
+}
+
+static void
+set_display_manager_seat (IndicatorSessionUsersDbus * self,
+ DisplayManagerSeat * dm_seat)
+{
+ priv_t * p = self->priv;
+
+ g_clear_object (&p->dm_seat);
+
+ if (dm_seat != NULL)
+ p->dm_seat = g_object_ref (dm_seat);
+}
+
+/***
+**** IndicatorSessionUsers virtual functions
+***/
+
+/* switch to (or create) a session for the specified user */
+static void
+my_activate_user (IndicatorSessionUsers * users, guint uid)
+{
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS(users);
+ priv_t * p = self->priv;
+ AccountsUser * au;
+ const char * username;
+
+ au = get_user_for_uid (self, uid);
+ username = au ? accounts_user_get_user_name (au) : NULL;
+
+ if (!username)
+ {
+ g_warning ("%s %s can't find user '%u'", G_STRLOC, G_STRFUNC, uid);
+ }
+ else
+ {
+ g_return_if_fail (p->dm_seat != NULL);
+
+ display_manager_seat_call_switch_to_user (p->dm_seat,
+ username,
+ "",
+ p->cancellable,
+ NULL,
+ NULL);
+ }
+}
+
+/* returns true if this is a live session */
+static gboolean
+my_is_live_session (IndicatorSessionUsers * users)
+{
+ g_return_val_if_fail (INDICATOR_IS_SESSION_USERS_DBUS(users), FALSE);
+
+ return INDICATOR_SESSION_USERS_DBUS(users)->priv->is_live;
+}
+
+/* get a list of our user ids */
+static GList *
+my_get_uids (IndicatorSessionUsers * users)
+{
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users);
+ return g_hash_table_get_keys (self->priv->uid_to_account);
+}
+
+/* build a new struct populated with info on the specified user */
+static IndicatorSessionUser *
+my_get_user (IndicatorSessionUsers * users, guint uid)
+{
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users);
+ priv_t * p = self->priv;
+ IndicatorSessionUser * ret;
+ AccountsUser * au;
+
+ ret = NULL;
+ au = get_user_for_uid (self, uid);
+ if (au && !accounts_user_get_system_account(au))
+ {
+ g_assert (uid == accounts_user_get_uid (au));
+
+ ret = g_new0 (IndicatorSessionUser, 1);
+ ret->uid = uid;
+ ret->user_name = g_strdup (accounts_user_get_user_name (au));
+ ret->real_name = g_strdup (accounts_user_get_real_name (au));
+ ret->icon_file = g_strdup (accounts_user_get_icon_file (au));
+ ret->login_frequency = accounts_user_get_login_frequency (au);
+ ret->is_logged_in = g_hash_table_contains (p->logins, GINT_TO_POINTER(uid));
+ ret->is_current_user = uid == p->active_uid;
+ }
+
+ return ret;
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o);
+ priv_t * p = self->priv;
+
+ if (p->update_list_tag != 0)
+ {
+ g_source_remove (p->update_list_tag);
+ p->update_list_tag = 0;
+ }
+
+ if (p->cancellable)
+ {
+ g_cancellable_cancel (p->cancellable);
+ g_clear_object (&p->cancellable);
+ }
+
+ set_account_manager (self, NULL);
+ set_display_manager_seat (self, NULL);
+ set_login1_seat (self, NULL);
+ set_login1_manager (self, NULL);
+
+ g_hash_table_remove_all (p->uid_to_account);
+
+ G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o);
+ priv_t * p = self->priv;
+
+ g_hash_table_destroy (p->logins);
+ g_hash_table_destroy (p->uid_to_account);
+
+ G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->finalize (o);
+}
+
+static void
+indicator_session_users_dbus_class_init (IndicatorSessionUsersDbusClass * klass)
+{
+ GObjectClass * object_class;
+ IndicatorSessionUsersClass * users_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+
+ users_class = INDICATOR_SESSION_USERS_CLASS (klass);
+ users_class->is_live_session = my_is_live_session;
+ users_class->get_uids = my_get_uids;
+ users_class->get_user = my_get_user;
+ users_class->activate_user = my_activate_user;
+
+ g_type_class_add_private (klass, sizeof (IndicatorSessionUsersDbusPriv));
+}
+
+static void
+indicator_session_users_dbus_init (IndicatorSessionUsersDbus * self)
+{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_SESSION_USERS_DBUS,
+ IndicatorSessionUsersDbusPriv);
+ self->priv = p;
+ p->cancellable = g_cancellable_new ();
+
+ p->uid_to_account = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)user_record_free);
+
+ p->logins = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+/***
+**** Public
+***/
+
+IndicatorSessionUsers *
+indicator_session_users_dbus_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_SESSION_USERS_DBUS, NULL);
+
+ return INDICATOR_SESSION_USERS (o);
+}
+
+void
+indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus * self,
+ Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * dm_seat,
+ Accounts * accounts)
+{
+ g_return_if_fail (INDICATOR_IS_SESSION_USERS_DBUS (self));
+
+ set_login1_manager (self, login1_manager);
+ set_login1_seat (self, login1_seat);
+ set_display_manager_seat (self, dm_seat);
+ set_account_manager (self, accounts);
+}
diff --git a/src/backend-dbus/users.h b/src/backend-dbus/users.h
new file mode 100644
index 0000000..d6c17df
--- /dev/null
+++ b/src/backend-dbus/users.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USERS_DBUS_H__
+#define __USERS_DBUS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "../users.h" /* parent class */
+#include "dbus-accounts.h"
+#include "dbus-login1-manager.h"
+#include "dbus-login1-seat.h"
+#include "dbus-display-manager.h"
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_SESSION_USERS_DBUS (indicator_session_users_dbus_get_type())
+#define INDICATOR_SESSION_USERS_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_USERS_DBUS, IndicatorSessionUsersDbus))
+#define INDICATOR_SESSION_USERS_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_USERS_DBUS, IndicatorSessionUsersDbusClass))
+#define INDICATOR_IS_SESSION_USERS_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_USERS_DBUS))
+
+typedef struct _IndicatorSessionUsersDbus IndicatorSessionUsersDbus;
+typedef struct _IndicatorSessionUsersDbusPriv IndicatorSessionUsersDbusPriv;
+typedef struct _IndicatorSessionUsersDbusClass IndicatorSessionUsersDbusClass;
+
+/**
+ * An implementation of IndicatorSessionUsers that gets its user information
+ * from org.freedesktop.login1 and org.freedesktop.Accounts over DBus.
+ */
+struct _IndicatorSessionUsersDbus
+{
+ /*< private >*/
+ IndicatorSessionUsers parent;
+ IndicatorSessionUsersDbusPriv * priv;
+};
+
+struct _IndicatorSessionUsersDbusClass
+{
+ IndicatorSessionUsersClass parent_class;
+};
+
+GType indicator_session_users_dbus_get_type (void);
+
+IndicatorSessionUsers * indicator_session_users_dbus_new (void);
+
+void indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus *,
+ Login1Manager *,
+ Login1Seat *,
+ DisplayManagerSeat *,
+ Accounts *);
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/backend-dbus/utils.c b/src/backend-dbus/utils.c
new file mode 100644
index 0000000..bc862e8
--- /dev/null
+++ b/src/backend-dbus/utils.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "utils.h"
+
+/***
+**** indicator_session_util_get_session_proxies()
+***/
+
+struct session_proxy_data
+{
+ Login1Manager * login1_manager;
+ Login1Seat * login1_seat;
+ DisplayManagerSeat * dm_seat;
+ Accounts * account_manager;
+
+ GCancellable * cancellable;
+ int pending;
+
+ indicator_session_util_session_proxies_func callback;
+ gpointer user_data;
+};
+
+
+static void
+on_proxy_ready_impl (struct session_proxy_data * data,
+ gsize member_offset,
+ GError * err,
+ gpointer proxy)
+{
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ *((gpointer*)G_STRUCT_MEMBER_P(data, member_offset)) = proxy;
+ }
+
+ if (!--data->pending)
+ {
+ data->callback (data->login1_manager,
+ data->login1_seat,
+ data->dm_seat,
+ data->account_manager,
+ data->cancellable,
+ data->user_data);
+
+ g_clear_object (&data->login1_manager);
+ g_clear_object (&data->login1_seat);
+ g_clear_object (&data->dm_seat);
+ g_clear_object (&data->account_manager);
+ g_clear_object (&data->cancellable);
+ g_free (data);
+ }
+}
+
+static void
+on_display_manager_seat_proxy_ready (GObject * o G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, dm_seat);
+ GError * err = NULL;
+ gpointer proxy = display_manager_seat_proxy_new_for_bus_finish (res, &err);
+ on_proxy_ready_impl (gdata, offset, err, proxy);
+}
+
+static void
+on_login1_seat_ready (GObject * o G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, login1_seat);
+ GError * err = NULL;
+ gpointer proxy = login1_seat_proxy_new_for_bus_finish (res, &err);
+ on_proxy_ready_impl (gdata, offset, err, proxy);
+}
+
+static void
+on_login1_manager_ready (GObject * o G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, login1_manager);
+ GError * err = NULL;
+ gpointer proxy = login1_manager_proxy_new_for_bus_finish (res, &err);
+ on_proxy_ready_impl (gdata, offset, err, proxy);
+}
+
+static void
+on_accounts_proxy_ready (GObject * o G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, account_manager);
+ GError * err = NULL;
+ gpointer proxy = accounts_proxy_new_for_bus_finish (res, &err);
+ on_proxy_ready_impl (gdata, offset, err, proxy);
+}
+
+/* helper utility to get the dbus proxies used by the backend-dbus classes */
+void
+indicator_session_util_get_session_proxies (
+ indicator_session_util_session_proxies_func func,
+ GCancellable * cancellable,
+ gpointer user_data)
+{
+ struct session_proxy_data * data;
+ const char * str;
+
+ data = g_new0 (struct session_proxy_data, 1);
+ data->callback = func;
+ data->user_data = user_data;
+ data->cancellable = g_object_ref (cancellable);
+
+ /* login1 */
+ data->pending++;
+ login1_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ data->cancellable,
+ on_login1_manager_ready, data);
+
+ /* login1 seat */
+ if ((str = g_getenv ("XDG_SEAT")))
+ {
+ char * path;
+ data->pending++;
+ path = g_strconcat ("/org/freedesktop/login1/seat/", str, NULL);
+ login1_seat_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+ "org.freedesktop.login1",
+ path,
+ data->cancellable,
+ on_login1_seat_ready,
+ data);
+ g_free (path);
+ }
+
+ /* Accounts */
+ data->pending++;
+ accounts_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+ "org.freedesktop.Accounts",
+ "/org/freedesktop/Accounts",
+ data->cancellable,
+ on_accounts_proxy_ready, data);
+
+ /* DisplayManager seat */
+ if ((str = g_getenv ("XDG_SEAT_PATH")))
+ {
+ data->pending++;
+ display_manager_seat_proxy_new_for_bus (
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+ "org.freedesktop.DisplayManager",
+ str,
+ data->cancellable,
+ on_display_manager_seat_proxy_ready, data);
+ }
+}
diff --git a/src/backend-dbus/utils.h b/src/backend-dbus/utils.h
new file mode 100644
index 0000000..802dd5e
--- /dev/null
+++ b/src/backend-dbus/utils.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DBUS_UTILS_H__
+#define __DBUS_UTILS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "dbus-accounts.h"
+#include "dbus-display-manager.h"
+#include "dbus-login1-manager.h"
+#include "dbus-login1-seat.h"
+
+typedef void (*indicator_session_util_session_proxies_func)(
+ Login1Manager * login1_manager,
+ Login1Seat * login1_seat,
+ DisplayManagerSeat * display_manager_seat,
+ Accounts * account_manager,
+ GCancellable * cancellable,
+ gpointer user_data);
+
+/**
+ * Both users-dbus and guest-dbus need some of these proxies.
+ * Getting them all involves a lot of steps, so instead of repeating
+ * ourselves, the common dbus steps are extracted to this func.
+ */
+void indicator_session_util_get_session_proxies (
+ indicator_session_util_session_proxies_func func,
+ GCancellable * cancellable,
+ gpointer user_data);
+
+#endif