From 33bac335dc8fc2545e4cb11269142f1e4163ed9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 14 Apr 2023 14:13:03 +0200 Subject: [PATCH] remote-desktop: Add persistent sessions This adds persistent remote desktop sessions similar to persistent screen cast sessions. What it means is that applications can ask for sessions to be "saved" either temporarily in the portal, or in the permission store, and later be restored at will, without any interactive dialog showing up. It works the same way, by the Start() method returning a restore token, and similarly to the screen cast variant, passing the same restore token to the SelectDevices() method the next time. Closes: https://github.com/flatpak/xdg-desktop-portal/issues/850 --- ....freedesktop.impl.portal.RemoteDesktop.xml | 41 +++++++ data/org.freedesktop.portal.RemoteDesktop.xml | 45 ++++++++ data/org.freedesktop.portal.ScreenCast.xml | 9 +- src/remote-desktop.c | 107 +++++++++++++++++- 4 files changed, 197 insertions(+), 5 deletions(-) diff --git a/data/org.freedesktop.impl.portal.RemoteDesktop.xml b/data/org.freedesktop.impl.portal.RemoteDesktop.xml index 0c6bdd609..e04c94141 100644 --- a/data/org.freedesktop.impl.portal.RemoteDesktop.xml +++ b/data/org.freedesktop.impl.portal.RemoteDesktop.xml @@ -81,6 +81,47 @@ Default is all. + + restore_data (suv) + + The data to restore from a previous session. + + + If the stored session cannot be restored, this value is ignored + and the user will be prompted normally. This may happen when, for + example, the session contains a monitor or a window that is not + available anymore, or when the stored permissions are withdrawn. + + + The restore data is composed of the vendor name (e.g. "GNOME" or + "KDE"), the version of the implementation-specific private data, + and the implementation-specific private data itself. + + + This option was added in version 2 of this interface. + + + + persist_mode u + + How this session should persist. Default is 0. Accepted values are: + + + + 0: Do not persist (default) + 1: Permissions persist as long as the application is running + 2: Permissions persist until explicitly revoked + + + + If the permission for the session to persist is granted, "restore_data" + will be returned via the #org.freedesktop.portal.Request::Response + signal of the #org.freedesktop.impl.portal.RemoteDesktop.Start method. + + + This option was added in version 2 of this interface. + + For available device types, see the AvailableDeviceTypes property. diff --git a/data/org.freedesktop.portal.RemoteDesktop.xml b/data/org.freedesktop.portal.RemoteDesktop.xml index 9e1346f84..02b7466c6 100644 --- a/data/org.freedesktop.portal.RemoteDesktop.xml +++ b/data/org.freedesktop.portal.RemoteDesktop.xml @@ -109,6 +109,41 @@ Default is all. + + restore_token s + + The token to restore a previous session. + + If the stored session cannot be restored, this value is ignored + and the user will be prompted normally. This may happen when, for + example, the session contains a monitor or a window that is not + available anymore, or when the stored permissions are withdrawn. + + The restore token is invalidated after using it once. To restore + the same session again, use the new restore token sent in response + to starting this session. + + This option was added in version 2 of this interface. + + + + persist_mode u + + How this session should persist. Default is 0. Accepted values are: + + + 0: Do not persist (default) + 1: Permissions persist as long as the application is running + 2: Permissions persist until explicitly revoked + + + If the permission for the session to persist is granted, a restore token will + be returned via the #org.freedesktop.portal.Request::Response signal of the + start method used to start the session. + + This option was added in version 2 of this interface. + + For available device types, see the AvailableDeviceTypes property. @@ -159,6 +194,16 @@ Since version 2. + + restore_token s + + The restore token. This token is a single use token that can later + be used to restore a session. See + org.freedesktop.portal.RemoteDesktop.SelectDevices() for details. + + This response option was added in version 2 of this interface. + + If a screen cast source was selected, the results of the diff --git a/data/org.freedesktop.portal.ScreenCast.xml b/data/org.freedesktop.portal.ScreenCast.xml index 75309313a..3fc1d4e2f 100644 --- a/data/org.freedesktop.portal.ScreenCast.xml +++ b/data/org.freedesktop.portal.ScreenCast.xml @@ -133,6 +133,10 @@ the same session again, use the new restore token sent in response to starting this session. + Setting a restore_token is only allowed for screen cast sessions. + Persistent remote desktop screen cast sessions can only be handled + via the #org.freedesktop.portal.RemoteDesktop interface. + This option was added in version 4 of this interface. @@ -147,8 +151,9 @@ 2: Permissions persist until explicitly revoked - Remote desktop screen cast sessions cannot persist. The only allowed - persist_mode for remote desktop sessions is 0. + Setting persist_mode is only allowed for screen cast sessions. Persistent + remote desktop screen cast sessions can only be handled via the + #org.freedesktop.portal.RemoteDesktop interface. If the permission for the session to persist is granted, a restore token will be returned via the #org.freedesktop.portal.Request::Response signal of the diff --git a/src/remote-desktop.c b/src/remote-desktop.c index 513ebf9b5..11b2a5c52 100644 --- a/src/remote-desktop.c +++ b/src/remote-desktop.c @@ -21,6 +21,7 @@ #include "remote-desktop.h" #include "screen-cast.h" #include "request.h" +#include "restore-token.h" #include "pipewire.h" #include "call.h" #include "session.h" @@ -30,6 +31,8 @@ #include +#define REMOTE_DESKTOP_TABLE "remote-desktop" + typedef struct _RemoteDesktop RemoteDesktop; typedef struct _RemoteDesktopClass RemoteDesktopClass; @@ -88,6 +91,10 @@ typedef struct _RemoteDesktopSession gboolean sources_selected; gboolean clipboard_enabled; + + char *restore_token; + PersistMode persist_mode; + GVariant *restore_data; } RemoteDesktopSession; typedef struct _RemoteDesktopSessionClass @@ -420,10 +427,75 @@ validate_device_types (const char *key, return TRUE; } +static gboolean +validate_restore_token (const char *key, + GVariant *value, + GVariant *options, + GError **error) +{ + const char *restore_token = g_variant_get_string (value, NULL); + + if (!g_uuid_string_is_valid (restore_token)) + { + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, + XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, + "Restore token is not a valid UUID string"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_persist_mode (const char *key, + GVariant *value, + GVariant *options, + GError **error) +{ + uint32_t mode = g_variant_get_uint32 (value); + + if (mode > PERSIST_MODE_PERSISTENT) + { + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, + XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, + "Invalid persist mode %x", mode); + return FALSE; + } + + return TRUE; +} + static XdpOptionKey remote_desktop_select_devices_options[] = { { "types", G_VARIANT_TYPE_UINT32, validate_device_types }, + { "restore_token", G_VARIANT_TYPE_STRING, validate_restore_token }, + { "persist_mode", G_VARIANT_TYPE_UINT32, validate_persist_mode }, }; +static gboolean +replace_remote_desktop_restore_token_with_data (Session *session, + GVariant **in_out_options, + GError **error) +{ + RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *) session; + g_autoptr(GVariant) options = NULL; + PersistMode persist_mode; + + options = *in_out_options; + + if (!g_variant_lookup (options, "persist_mode", "u", &persist_mode)) + persist_mode = PERSIST_MODE_NONE; + + remote_desktop_session->persist_mode = persist_mode; + replace_restore_token_with_data (session, + REMOTE_DESKTOP_TABLE, + in_out_options, + &remote_desktop_session->restore_token); + + return TRUE; +} + static gboolean handle_select_devices (XdpDbusRemoteDesktop *object, GDBusMethodInvocation *invocation, @@ -436,6 +508,7 @@ handle_select_devices (XdpDbusRemoteDesktop *object, g_autoptr(GError) error = NULL; g_autoptr(XdpDbusImplRequest) impl_request = NULL; GVariantBuilder options_builder; + GVariant *options; REQUEST_AUTOLOCK (request); @@ -487,6 +560,18 @@ handle_select_devices (XdpDbusRemoteDesktop *object, return G_DBUS_METHOD_INVOCATION_HANDLED; } + options = g_variant_builder_end (&options_builder); + + /* If 'restore_token' is passed, lookup the corresponding data in the + * permission store and / or the GHashTable with transient permissions. + * Portal implementations do not have access to the restore token. + */ + if (!replace_remote_desktop_restore_token_with_data (session, &options, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + g_object_set_qdata_full (G_OBJECT (request), quark_request_session, g_object_ref (session), @@ -496,7 +581,7 @@ handle_select_devices (XdpDbusRemoteDesktop *object, request->id, arg_session_handle, xdp_app_info_get_id (request->app_info), - g_variant_builder_end (&options_builder), + options, NULL, select_devices_done, g_object_ref (request)); @@ -506,12 +591,25 @@ handle_select_devices (XdpDbusRemoteDesktop *object, return G_DBUS_METHOD_INVOCATION_HANDLED; } +static void +replace_restore_remote_desktop_data_with_token (RemoteDesktopSession *remote_desktop_session, + GVariant **in_out_results) +{ + replace_restore_data_with_token ((Session *) remote_desktop_session, + REMOTE_DESKTOP_TABLE, + in_out_results, + &remote_desktop_session->persist_mode, + &remote_desktop_session->restore_token, + &remote_desktop_session->restore_data); +} + static gboolean process_results (RemoteDesktopSession *remote_desktop_session, - GVariant *results, + GVariant **in_out_results, GError **error) { g_autoptr(GVariantIter) streams_iter = NULL; + GVariant *results = *in_out_results; uint32_t devices = 0; gboolean clipboard_enabled = FALSE; @@ -527,6 +625,9 @@ process_results (RemoteDesktopSession *remote_desktop_session, if (g_variant_lookup (results, "clipboard_enabled", "b", &clipboard_enabled)) remote_desktop_session->clipboard_enabled = clipboard_enabled; + replace_restore_remote_desktop_data_with_token (remote_desktop_session, + in_out_results); + return TRUE; } @@ -566,7 +667,7 @@ start_done (GObject *source_object, { if (response == 0) { - if (!process_results (remote_desktop_session, results, &error)) + if (!process_results (remote_desktop_session, &results, &error)) { g_warning ("Could not start remote desktop session: %s", error->message);