/*
* Copyright © 2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Felipe Borges
*/
#include "config.h"
#include
#include
#include
#include
#include
#include "wallpaper.h"
#include "permissions.h"
#include "request.h"
#include "xdp-dbus.h"
#include "xdp-impl-dbus.h"
#include "xdp-utils.h"
#define PERMISSION_TABLE "wallpaper"
#define PERMISSION_ID "wallpaper"
typedef struct _Wallpaper Wallpaper;
typedef struct _WallpaperClass WallpaperClass;
struct _Wallpaper
{
XdpDbusWallpaperSkeleton parent_instance;
};
struct _WallpaperClass
{
XdpDbusWallpaperSkeletonClass parent_class;
};
static XdpDbusImplWallpaper *impl;
static XdpDbusImplAccess *access_impl;
static Wallpaper *wallpaper;
GType wallpaper_get_type (void) G_GNUC_CONST;
static void wallpaper_iface_init (XdpDbusWallpaperIface *iface);
G_DEFINE_TYPE_WITH_CODE (Wallpaper, wallpaper, XDP_DBUS_TYPE_WALLPAPER_SKELETON,
G_IMPLEMENT_INTERFACE (XDP_DBUS_TYPE_WALLPAPER,
wallpaper_iface_init));
static void
send_response (Request *request,
guint response)
{
if (request->exported)
{
GVariantBuilder opt_builder;
g_debug ("sending response: %d", response);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
xdp_dbus_request_emit_response (XDP_DBUS_REQUEST (request),
response,
g_variant_builder_end (&opt_builder));
request_unexport (request);
}
}
static void
handle_set_wallpaper_uri_done (GObject *source,
GAsyncResult *result,
gpointer data)
{
guint response = 2;
g_autoptr(GError) error = NULL;
Request *request = data;
if (!xdp_dbus_impl_wallpaper_call_set_wallpaper_uri_finish (XDP_DBUS_IMPL_WALLPAPER (source),
&response,
result,
&error))
{
g_dbus_error_strip_remote_error (error);
g_warning ("A backend call failed: %s", error->message);
}
send_response (request, response);
g_object_unref (request);
}
static gboolean
validate_set_on (const char *key,
GVariant *value,
GVariant *options,
GError **error)
{
const char *string = g_variant_get_string (value, NULL);
return ((g_strcmp0 (string, "both") == 0) ||
(g_strcmp0 (string, "background") == 0) ||
(g_strcmp0 (string, "lockscreen") == 0));
}
static XdpOptionKey wallpaper_options[] = {
{ "show-preview", G_VARIANT_TYPE_BOOLEAN, NULL },
{ "set-on", G_VARIANT_TYPE_STRING, validate_set_on }
};
static void
handle_set_wallpaper_in_thread_func (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
Request *request = (Request *)task_data;
const char *parent_window;
const char *id = xdp_app_info_get_id (request->app_info);
g_autoptr(GError) error = NULL;
g_autofree char *uri = NULL;
GVariantBuilder opt_builder;
g_autoptr(XdpDbusImplRequest) impl_request = NULL;
GVariant *options;
gboolean show_preview = FALSE;
int fd;
Permission permission;
REQUEST_AUTOLOCK (request);
parent_window = ((const char *)g_object_get_data (G_OBJECT (request), "parent-window"));
uri = g_strdup ((const char *)g_object_get_data (G_OBJECT (request), "uri"));
fd = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (request), "fd"));
options = ((GVariant *)g_object_get_data (G_OBJECT (request), "options"));
if (uri != NULL && fd != -1)
{
g_warning ("Rejecting invalid set-wallpaper request (both URI and fd are set)");
if (request->exported)
{
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
xdp_dbus_request_emit_response (XDP_DBUS_REQUEST (request),
XDG_DESKTOP_PORTAL_RESPONSE_OTHER,
g_variant_builder_end (&opt_builder));
request_unexport (request);
}
return;
}
permission = get_permission_sync (id, PERMISSION_TABLE, PERMISSION_ID);
if (permission == PERMISSION_NO)
{
send_response (request, 2);
return;
}
g_variant_lookup (options, "show-preview", "b", &show_preview);
if (!show_preview && permission != PERMISSION_YES)
{
guint access_response = 2;
g_autoptr(GVariant) access_results = NULL;
GVariantBuilder access_opt_builder;
g_autofree gchar *app_id = NULL;
g_autofree gchar *title = NULL;
g_autofree gchar *subtitle = NULL;
const gchar *body;
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 ("preferences-desktop-wallpaper-symbolic"));
if (g_strcmp0 (id, "") != 0)
{
g_autoptr(GAppInfo) info = NULL;
const gchar *name = NULL;
info = xdp_app_info_load_app_info (request->app_info);
if (info)
{
name = g_app_info_get_display_name (G_APP_INFO (info));
app_id = xdp_get_app_id_from_desktop_id (g_app_info_get_id (info));
}
else
{
name = id;
app_id = g_strdup (id);
}
title = g_strdup_printf (_("Allow %s to Set Backgrounds?"), name);
subtitle = g_strdup_printf (_("%s is requesting to be able to change the background image."), 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));
app_id = g_strdup ("");
title = g_strdup (_("Allow Applications to Set Backgrounds?"));
subtitle = g_strdup (_("An application is requesting to be able to change the background image."));
}
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);
send_response (request, 2);
return;
}
if (permission == PERMISSION_UNSET)
set_permission_sync (id, PERMISSION_TABLE, PERMISSION_ID, access_response == 0 ? PERMISSION_YES : PERMISSION_NO);
if (access_response != 0)
{
send_response (request, 2);
return;
}
}
if (!uri)
{
g_autofree char *path = NULL;
path = xdp_app_info_get_path_for_fd (request->app_info, fd, 0, NULL, NULL, &error);
if (path == NULL)
{
g_debug ("Cannot get path for fd: %s", error->message);
/* Reject the request */
if (request->exported)
{
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
xdp_dbus_request_emit_response (XDP_DBUS_REQUEST (request),
XDG_DESKTOP_PORTAL_RESPONSE_OTHER,
g_variant_builder_end (&opt_builder));
request_unexport (request);
}
return;
}
uri = g_filename_to_uri (path, NULL, NULL);
g_object_set_data_full (G_OBJECT (request), "uri", g_strdup (uri), g_free);
close (fd);
g_object_set_data (G_OBJECT (request), "fd", GINT_TO_POINTER (-1));
}
impl_request = xdp_dbus_impl_request_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (impl)),
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
g_dbus_proxy_get_name (G_DBUS_PROXY (impl)),
request->id,
NULL, &error);
if (!impl_request)
{
g_warning ("Failed to to create wallpaper implementation proxy: %s", error->message);
send_response (request, 2);
return;
}
request_set_impl_request (request, impl_request);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
xdp_filter_options (options, &opt_builder,
wallpaper_options, G_N_ELEMENTS (wallpaper_options),
NULL);
g_debug ("Calling SetWallpaperURI with %s", uri);
xdp_dbus_impl_wallpaper_call_set_wallpaper_uri (impl,
request->id,
id,
parent_window,
uri,
g_variant_builder_end (&opt_builder),
NULL,
handle_set_wallpaper_uri_done,
g_object_ref (request));
}
static gboolean
handle_set_wallpaper_uri (XdpDbusWallpaper *object,
GDBusMethodInvocation *invocation,
const char *arg_parent_window,
const char *arg_uri,
GVariant *arg_options)
{
Request *request = request_from_invocation (invocation);
g_autoptr(GTask) task = NULL;
g_debug ("Handle SetWallpaperURI");
g_object_set_data_full (G_OBJECT (request), "uri", g_strdup (arg_uri), g_free);
g_object_set_data_full (G_OBJECT (request), "parent-window", g_strdup (arg_parent_window), g_free);
g_object_set_data_full (G_OBJECT (request),
"options",
g_variant_ref (arg_options),
(GDestroyNotify)g_variant_unref);
request_export (request, g_dbus_method_invocation_get_connection (invocation));
xdp_dbus_wallpaper_complete_set_wallpaper_uri (object, invocation, request->id);
task = g_task_new (object, NULL, NULL, NULL);
g_task_set_task_data (task, g_object_ref (request), g_object_unref);
g_task_run_in_thread (task, handle_set_wallpaper_in_thread_func);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_set_wallpaper_file (XdpDbusWallpaper *object,
GDBusMethodInvocation *invocation,
GUnixFDList *fd_list,
const char *arg_parent_window,
GVariant *arg_fd,
GVariant *arg_options)
{
Request *request = request_from_invocation (invocation);
g_autoptr(GTask) task = NULL;
int fd_id, fd;
g_autoptr(GError) error = NULL;
g_debug ("Handle SetWallpaperFile");
g_variant_get (arg_fd, "h", &fd_id);
fd = g_unix_fd_list_get (fd_list, fd_id, &error);
if (fd == -1)
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_object_set_data (G_OBJECT (request), "fd", GINT_TO_POINTER (fd));
g_object_set_data_full (G_OBJECT (request), "parent-window", g_strdup (arg_parent_window), g_free);
g_object_set_data_full (G_OBJECT (request),
"options",
g_variant_ref (arg_options),
(GDestroyNotify)g_variant_unref);
request_export (request, g_dbus_method_invocation_get_connection (invocation));
xdp_dbus_wallpaper_complete_set_wallpaper_file (object, invocation, NULL, request->id);
task = g_task_new (object, NULL, NULL, NULL);
g_task_set_task_data (task, g_object_ref (request), g_object_unref);
g_task_run_in_thread (task, handle_set_wallpaper_in_thread_func);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static void
wallpaper_iface_init (XdpDbusWallpaperIface *iface)
{
iface->handle_set_wallpaper_uri = handle_set_wallpaper_uri;
iface->handle_set_wallpaper_file = handle_set_wallpaper_file;
}
static void
wallpaper_init (Wallpaper *wallpaper)
{
xdp_dbus_wallpaper_set_version (XDP_DBUS_WALLPAPER (wallpaper), 1);
}
static void
wallpaper_class_init (WallpaperClass *klass)
{
}
GDBusInterfaceSkeleton *
wallpaper_create (GDBusConnection *connection,
const char *dbus_name_access,
const char *dbus_name_wallpaper)
{
g_autoptr(GError) error = NULL;
impl = xdp_dbus_impl_wallpaper_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
dbus_name_wallpaper,
DESKTOP_PORTAL_OBJECT_PATH,
NULL,
&error);
if (impl == NULL)
{
g_warning ("Failed to create wallpaper proxy: %s", error->message);
return NULL;
}
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (impl), G_MAXINT);
wallpaper = g_object_new (wallpaper_get_type (), NULL);
access_impl = xdp_dbus_impl_access_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
dbus_name_access,
DESKTOP_PORTAL_OBJECT_PATH,
NULL,
&error);
return G_DBUS_INTERFACE_SKELETON (wallpaper);
}