Skip to content

Commit

Permalink
screenshot: Request permission for non-interactive screenshots
Browse files Browse the repository at this point in the history
This commits makes the screenshot portal request a permission
using the the access portal when a non-interactive screenshot
is requested.

This should be enough to let applications request permissions
once, and be forever able to screenshot without much hassle.

Add a new 'version' property to the Screenshot implementation
D-Bus interface, and check if the implementation reports at
least version = 2 before requesting the screenshot permission.

Fixes #649
  • Loading branch information
GeorgesStavracas committed Aug 10, 2022
1 parent 516e6b4 commit c827417
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 4 deletions.
13 changes: 13 additions & 0 deletions data/org.freedesktop.impl.portal.Screenshot.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
@short_description: Screenshot portal backend interface
This screenshot portal lets sandboxed applications request a screenshot.
This documentation describes version 2 of this interface.
-->
<interface name="org.freedesktop.impl.portal.Screenshot">
<!--
Expand Down Expand Up @@ -52,6 +54,15 @@
Defaults to no.
</para></listitem>
</varlistentry>
<varlistentry>
<term>permission_store_checked b</term>
<listitem><para>
Hint whether the screenshot portal has checked the 'screenshot' permission for
the requesting app. Defaults to no.
This option was added in version 2 of this interface.
</para></listitem>
</varlistentry>
</variablelist>
The following results get returned via the @results vardict:
Expand Down Expand Up @@ -103,5 +114,7 @@
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
<arg type="a{sv}" name="results" direction="out"/>
</method>

<property name="version" type="u" access="read"/>
</interface>
</node>
111 changes: 107 additions & 4 deletions src/screenshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@
#include <sys/stat.h>
#include <fcntl.h>

#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>

#include "screenshot.h"
#include "permissions.h"
#include "request.h"
#include "documents.h"
#include "xdp-dbus.h"
#include "xdp-impl-dbus.h"
#include "xdp-utils.h"

#define PERMISSION_TABLE "screenshot"
#define PERMISSION_ID "screenshot"

typedef struct _Screenshot Screenshot;
typedef struct _ScreenshotClass ScreenshotClass;

Expand Down Expand Up @@ -186,14 +192,108 @@ handle_screenshot_in_thread_func (GTask *task,
g_autoptr(GError) error = NULL;
g_autoptr(XdpDbusImplRequest) impl_request = NULL;
GVariantBuilder opt_builder;
Permission permission;
GVariant *options;
gboolean permission_store_checked = FALSE;
gboolean interactive;
const char *parent_window;
const char *app_id;

REQUEST_AUTOLOCK (request);

g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);

app_id = xdp_app_info_get_id (request->app_info);
parent_window = ((const char *)g_object_get_data (G_OBJECT (request), "parent-window"));
options = ((GVariant *)g_object_get_data (G_OBJECT (request), "options"));

if (xdp_dbus_impl_screenshot_get_version (impl) < 2)
goto query_impl;

permission = get_permission_sync (app_id, PERMISSION_TABLE, PERMISSION_ID);

if (!g_variant_lookup (options, "interactive", "b", &interactive))
interactive = FALSE;

if (!interactive && permission != PERMISSION_YES)
{
g_autoptr(GVariant) access_results = NULL;
GVariantBuilder access_opt_builder;
g_autofree gchar *subtitle = NULL;
g_autofree gchar *title = NULL;
const gchar *body;
guint access_response = 2;

if (permission == PERMISSION_NO)
{
send_response (request, 2, g_variant_builder_end (&opt_builder));
return;
}

g_variant_builder_init (&access_opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&access_opt_builder, "{sv}",
"deny_label", g_variant_new_string (_("Deny")));
g_variant_builder_add (&access_opt_builder, "{sv}",
"grant_label", g_variant_new_string (_("Allow")));
g_variant_builder_add (&access_opt_builder, "{sv}",
"icon", g_variant_new_string ("applets-screenshooter-symbolic"));

if (g_strcmp0 (app_id, "") != 0)
{
g_autoptr(GDesktopAppInfo) info = NULL;
g_autofree gchar *id = NULL;
const gchar *name;

id = g_strconcat (app_id, ".desktop", NULL);
info = g_desktop_app_info_new (id);
name = g_app_info_get_display_name (G_APP_INFO (info));

title = g_strdup_printf (_("Allow %s to Take Screenshots?"), name);
subtitle = g_strdup_printf (_("%s wants to be able to take screenshots at any time."), name);
}
else
{
/* Note: this will set the wallpaper permission for all unsandboxed
* apps for which an app ID can't be determined.
*/
g_assert (xdp_app_info_is_host (request->app_info));
title = g_strdup (_("Allow Applications to Take Screenshots?"));
subtitle = g_strdup (_("An application wants to be able to take screenshots at any time."));
}

body = _("This permission can be changed at any time from the privacy settings.");

if (!xdp_dbus_impl_access_call_access_dialog_sync (access_impl,
request->id,
app_id,
parent_window,
title,
subtitle,
body,
g_variant_builder_end (&access_opt_builder),
&access_response,
&access_results,
NULL,
&error))
{
g_warning ("Failed to show access dialog: %s", error->message);
return;
}

if (permission == PERMISSION_UNSET)
set_permission_sync (app_id, PERMISSION_TABLE, PERMISSION_ID, access_response == 0 ? PERMISSION_YES : PERMISSION_NO);

if (access_response != 0)
{
send_response (request, 2, g_variant_builder_end (&opt_builder));
return;
}
}

permission_store_checked = TRUE;

query_impl:

impl_request =
xdp_dbus_impl_request_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (impl)),
G_DBUS_PROXY_FLAGS_NONE,
Expand All @@ -202,22 +302,25 @@ handle_screenshot_in_thread_func (GTask *task,
NULL, &error);
if (!impl_request)
{
g_warning ("Failed to to create screencast implementation proxy: %s", error->message);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_warning ("Failed to to create screenshot implementation proxy: %s", error->message);
send_response (request, 2, g_variant_builder_end (&opt_builder));
return;
}

request_set_impl_request (request, impl_request);

g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
xdp_filter_options (options, &opt_builder,
screenshot_options, G_N_ELEMENTS (screenshot_options),
NULL);
if (permission_store_checked)
{
g_variant_builder_add (&opt_builder, "{sv}", "permission_store_checked",
g_variant_new_boolean (TRUE));
}

xdp_dbus_impl_screenshot_call_screenshot (impl,
request->id,
xdp_app_info_get_id (request->app_info),
app_id,
parent_window,
g_variant_builder_end (&opt_builder),
NULL,
Expand Down
85 changes: 85 additions & 0 deletions tests/screenshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,38 @@
#include "screenshot.h"

#include <libportal/portal.h>
#include "xdp-impl-dbus.h"

extern char outdir[];

static int got_info;

extern XdpDbusImplPermissionStore *permission_store;

static void
set_screenshot_permissions (const char *permission)
{
const char *permissions[2] = { NULL, NULL };
g_autoptr(GError) error = NULL;

permissions[0] = permission;
xdp_dbus_impl_permission_store_call_set_permission_sync (permission_store,
"screenshot",
TRUE,
"screenshot",
"",
permissions,
NULL,
&error);
g_assert_no_error (error);
}

static void
reset_screenshot_permissions (void)
{
set_screenshot_permissions (NULL);
}

static void
screenshot_cb (GObject *obj,
GAsyncResult *result,
Expand Down Expand Up @@ -52,6 +79,15 @@ test_screenshot_basic (void)

keyfile = g_key_file_new ();

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_integer (keyfile, "result", "response", 0);

path = g_build_filename (outdir, "access", NULL);
g_key_file_save_to_file (keyfile, path, &error);
g_assert_no_error (error);
g_free (path);

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_string (keyfile, "result", "uri", "file://test/image");
Expand Down Expand Up @@ -83,7 +119,19 @@ test_screenshot_delay (void)
g_autoptr(GError) error = NULL;
g_autofree char *path = NULL;

reset_screenshot_permissions ();

keyfile = g_key_file_new ();

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_integer (keyfile, "result", "response", 0);

path = g_build_filename (outdir, "access", NULL);
g_key_file_save_to_file (keyfile, path, &error);
g_assert_no_error (error);
g_free (path);

g_key_file_set_integer (keyfile, "backend", "delay", 200);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_integer (keyfile, "result", "response", 0);
Expand Down Expand Up @@ -115,7 +163,19 @@ test_screenshot_cancel (void)
g_autoptr(GError) error = NULL;
g_autofree char *path = NULL;

reset_screenshot_permissions ();

keyfile = g_key_file_new ();

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_integer (keyfile, "result", "response", 0);

path = g_build_filename (outdir, "access", NULL);
g_key_file_save_to_file (keyfile, path, &error);
g_assert_no_error (error);
g_free (path);

g_key_file_set_integer (keyfile, "backend", "delay", 200);
g_key_file_set_integer (keyfile, "backend", "response", 1);
g_key_file_set_integer (keyfile, "result", "response", 1);
Expand Down Expand Up @@ -159,7 +219,19 @@ test_screenshot_close (void)
g_autofree char *path = NULL;
g_autoptr(GCancellable) cancellable = NULL;

reset_screenshot_permissions ();

keyfile = g_key_file_new ();

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_integer (keyfile, "result", "response", 0);

path = g_build_filename (outdir, "access", NULL);
g_key_file_save_to_file (keyfile, path, &error);
g_assert_no_error (error);
g_free (path);

g_key_file_set_integer (keyfile, "backend", "delay", 200);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_boolean (keyfile, "backend", "expect-close", 1);
Expand Down Expand Up @@ -190,8 +262,19 @@ test_screenshot_parallel (void)
g_autoptr(GError) error = NULL;
g_autofree char *path = NULL;

reset_screenshot_permissions ();

keyfile = g_key_file_new ();

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_integer (keyfile, "result", "response", 0);

path = g_build_filename (outdir, "access", NULL);
g_key_file_save_to_file (keyfile, path, &error);
g_assert_no_error (error);
g_free (path);

g_key_file_set_integer (keyfile, "backend", "delay", 0);
g_key_file_set_integer (keyfile, "backend", "response", 0);
g_key_file_set_string (keyfile, "result", "uri", "file://test/image");
Expand Down Expand Up @@ -393,6 +476,8 @@ test_color_parallel (void)
g_autoptr(GError) error = NULL;
g_autofree char *path = NULL;

set_screenshot_permissions ("no");

keyfile = g_key_file_new ();

g_key_file_set_integer (keyfile, "backend", "delay", 0);
Expand Down

0 comments on commit c827417

Please sign in to comment.