diff options
author | Matthias Clasen <mclasen@redhat.com> | 2017-05-20 16:21:14 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2017-05-26 18:21:32 -0400 |
commit | 0bc386c4cba383e4afb4baa11ac6802dd797caf8 (patch) | |
tree | a550a7ca8230f0f218ebf77781dbe5247fae1fcd | |
parent | b5e8e4eea95aa429897ecb8a931d8985edb8b4c2 (diff) | |
download | glib-open-file.tar.gz |
Use OpenFile for local filesopen-file
The OpenURI portal has a separate method to handle local
files now. Use it. At the same time, split out the openuri
helpers into separate files, and generate code for the
OpenURI portal.
-rw-r--r-- | gio/Makefile.am | 4 | ||||
-rw-r--r-- | gio/gappinfo.c | 264 | ||||
-rw-r--r-- | gio/gopenuriportal.c | 287 | ||||
-rw-r--r-- | gio/gopenuriportal.h | 41 | ||||
-rw-r--r-- | gio/org.freedesktop.portal.OpenURI.xml | 105 |
5 files changed, 456 insertions, 245 deletions
diff --git a/gio/Makefile.am b/gio/Makefile.am index 67db1b740..227df63c8 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -281,6 +281,8 @@ unix_sources = \ gportalnotificationbackend.c \ gdocumentportal.c \ gdocumentportal.h \ + gopenuriportal.c \ + gopenuriportal.h \ gportalsupport.c \ gportalsupport.h \ $(portal_sources) \ @@ -368,6 +370,7 @@ CLEANFILES += $(xdp_dbus_built_sources) portal_interfaces = \ org.freedesktop.portal.Documents.xml \ + org.freedesktop.portal.OpenURI.xml \ org.freedesktop.portal.NetworkMonitor.xml \ org.freedesktop.portal.ProxyResolver.xml \ $(NULL) @@ -383,6 +386,7 @@ $(xdp_dbus_built_sources) : $(portal_interfaces) --generate-c-code $(builddir)/xdp-dbus \ --annotate "org.freedesktop.portal.Documents.Add()" "org.gtk.GDBus.C.UnixFD" "true" \ --annotate "org.freedesktop.portal.Documents.AddNamed()" "org.gtk.GDBus.C.UnixFD" "true" \ + --annotate "org.freedesktop.portal.OpenURI.OpenFile()" "org.gtk.GDBus.C.UnixFD" "true" \ $^ portal_sources = \ diff --git a/gio/gappinfo.c b/gio/gappinfo.c index ba5815568..e910c0fa1 100644 --- a/gio/gappinfo.c +++ b/gio/gappinfo.c @@ -32,15 +32,12 @@ #ifdef G_OS_UNIX #include "gdbusconnection.h" #include "gdbusmessage.h" -#include "gdocumentportal.h" #include "gportalsupport.h" -#endif - -#ifdef G_OS_UNIX -#define FLATPAK_OPENURI_PORTAL_BUS_NAME "org.freedesktop.portal.Desktop" -#define FLATPAK_OPENURI_PORTAL_PATH "/org/freedesktop/portal/desktop" -#define FLATPAK_OPENURI_PORTAL_IFACE "org.freedesktop.portal.OpenURI" -#define FLATPAK_OPENURI_PORTAL_METHOD "OpenURI" +#include "gunixfdlist.h" +#include "gopenuriportal.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #endif /** @@ -687,240 +684,6 @@ g_app_info_should_show (GAppInfo *appinfo) return (* iface->should_show) (appinfo); } -#ifdef G_OS_UNIX -static void -response_received (GDBusConnection *connection, - const char *sender_name, - const char *object_path, - const char *interface_name, - const char *signal_name, - GVariant *parameters, - gpointer user_data) -{ - GTask *task = user_data; - guint32 response; - guint signal_id; - - signal_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task), "signal-id")); - g_dbus_connection_signal_unsubscribe (connection, signal_id); - - g_variant_get (parameters, "(u@a{sv})", &response, NULL); - - if (response == 0) - g_task_return_boolean (task, TRUE); - else if (response == 1) - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Launch cancelled"); - else - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Launch failed"); - - g_object_unref (task); -} - -static void -open_uri_done (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GDBusConnection *connection = G_DBUS_CONNECTION (source); - GTask *task = user_data; - GVariant *res; - GError *error = NULL; - const char *path; - guint signal_id; - - res = g_dbus_connection_call_finish (connection, result, &error); - - if (res == NULL) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_variant_get (res, "(&o)", &path); - - signal_id = - g_dbus_connection_signal_subscribe (connection, - "org.freedesktop.portal.Desktop", - "org.freedesktop.portal.Request", - "Response", - path, - NULL, - G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, - response_received, - task, NULL); - - g_object_set_data (G_OBJECT (task), "signal-id", GINT_TO_POINTER (signal_id)); - - g_variant_unref (res); -} - -static char * -real_uri_for_portal (const char *uri, - GAppLaunchContext *context, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - GError **error) -{ - GFile *file = NULL; - char *real_uri = NULL; - - file = g_file_new_for_uri (uri); - if (g_file_is_native (file)) - { - real_uri = g_document_portal_add_document (file, error); - g_object_unref (file); - - if (real_uri == NULL) - { - g_task_report_error (context, callback, user_data, NULL, *error); - return NULL; - } - } - else - { - g_object_unref (file); - real_uri = g_strdup (uri); - } - - return real_uri; -} - -static void -launch_default_with_portal_async (const char *uri, - GAppLaunchContext *context, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GDBusConnection *session_bus; - GVariantBuilder opt_builder; - const char *parent_window = NULL; - char *real_uri; - GTask *task; - GAsyncReadyCallback dbus_callback; - GError *error = NULL; - - session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - if (session_bus == NULL) - { - g_task_report_error (context, callback, user_data, NULL, error); - return; - } - - if (context && context->priv->envp) - parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID"); - - real_uri = real_uri_for_portal (uri, context, cancellable, callback, user_data, &error); - if (real_uri == NULL) - { - g_object_unref (session_bus); - return; - } - - g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); - - if (callback) - { - task = g_task_new (context, cancellable, callback, user_data); - dbus_callback = open_uri_done; - } - else - { - task = NULL; - dbus_callback = NULL; - } - - g_dbus_connection_call (session_bus, - FLATPAK_OPENURI_PORTAL_BUS_NAME, - FLATPAK_OPENURI_PORTAL_PATH, - FLATPAK_OPENURI_PORTAL_IFACE, - FLATPAK_OPENURI_PORTAL_METHOD, - g_variant_new ("(ss@a{sv})", - parent_window ? parent_window : "", - real_uri, - g_variant_builder_end (&opt_builder)), - NULL, - G_DBUS_CALL_FLAGS_NONE, - G_MAXINT, - cancellable, - dbus_callback, - task); - - g_dbus_connection_flush (session_bus, cancellable, NULL, NULL); - g_object_unref (session_bus); - g_free (real_uri); -} - -static void -launch_default_with_portal_sync (const char *uri, - GAppLaunchContext *context) -{ - GDBusConnection *session_bus; - GVariantBuilder opt_builder; - GVariant *res = NULL; - const char *parent_window = NULL; - char *real_uri; - GError *error = NULL; - - session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - if (session_bus == NULL) - { - g_task_report_error (context, NULL, NULL, NULL, error); - return; - } - - if (context && context->priv->envp) - parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID"); - - real_uri = real_uri_for_portal (uri, context, NULL, NULL, NULL, &error); - if (real_uri == NULL) - { - g_object_unref (session_bus); - return; - } - - g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); - - /* Calling the D-Bus method for the OpenURI portal "protects" the logic from - * not ever having the remote method running in case the xdg-desktop-portal - * process is not yet running and the caller quits quickly after the call. - */ - res = g_dbus_connection_call_sync (session_bus, - FLATPAK_OPENURI_PORTAL_BUS_NAME, - FLATPAK_OPENURI_PORTAL_PATH, - FLATPAK_OPENURI_PORTAL_IFACE, - FLATPAK_OPENURI_PORTAL_METHOD, - g_variant_new ("(ss@a{sv})", - parent_window ? parent_window : "", - real_uri, - g_variant_builder_end (&opt_builder)), - NULL, - G_DBUS_CALL_FLAGS_NONE, - G_MAXINT, - NULL, - &error); - if (res == NULL) - g_task_report_error (context, NULL, NULL, NULL, error); - else - g_variant_unref (res); - - g_dbus_connection_flush (session_bus, NULL, NULL, NULL); - g_object_unref (session_bus); - g_free (real_uri); -} - -static gboolean -launch_default_with_portal (const char *uri, - GAppLaunchContext *context, - GError **error) -{ - launch_default_with_portal_sync (uri, context); - return TRUE; -} -#endif - static gboolean launch_default_for_uri (const char *uri, GAppLaunchContext *context, @@ -985,10 +748,16 @@ g_app_info_launch_default_for_uri (const char *uri, #ifdef G_OS_UNIX if (glib_should_use_portal ()) { + const char *parent_window = NULL; + /* Reset any error previously set by launch_default_for_uri */ g_clear_error (error); - return launch_default_with_portal (uri, launch_context, error); + if (launch_context && launch_context->priv->envp) + parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID"); + + return g_openuri_portal_open_uri (uri, parent_window, error); + } #endif @@ -1028,7 +797,12 @@ g_app_info_launch_default_for_uri_async (const char *uri, #ifdef G_OS_UNIX if (!res && glib_should_use_portal ()) { - launch_default_with_portal_async (uri, context, cancellable, callback, user_data); + const char *parent_window = NULL; + + if (context && context->priv->envp) + parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID"); + + g_openuri_portal_open_uri_async (uri, parent_window, cancellable, callback, user_data); return; } #endif @@ -1057,7 +831,7 @@ gboolean g_app_info_launch_default_for_uri_finish (GAsyncResult *result, GError **error) { - return g_task_propagate_boolean (G_TASK (result), error); + return g_openuri_portal_open_uri_finish (result, error); } /** diff --git a/gio/gopenuriportal.c b/gio/gopenuriportal.c new file mode 100644 index 000000000..8c2d97693 --- /dev/null +++ b/gio/gopenuriportal.c @@ -0,0 +1,287 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2017 Red Hat, Inc. + * + * This library 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 <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include "gopenuriportal.h" +#include "xdp-dbus.h" +#include "gstdio.h" + +#ifdef G_OS_UNIX +#include "gunixfdlist.h" +#endif + +#ifndef O_PATH +#define O_PATH 0 +#endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#else +#define HAVE_O_CLOEXEC 1 +#endif + +static GXdpOpenURI *openuri; + +static gboolean +init_openuri_portal (void) +{ + static gsize openuri_inited = 0; + + if (g_once_init_enter (&openuri_inited)) + { + GError *error = NULL; + GDBusConnection *connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (connection != NULL) + { + openuri = gxdp_open_uri_proxy_new_sync (connection, 0, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + NULL, &error); + if (openuri == NULL) + { + g_warning ("Cannot create document portal proxy: %s", error->message); + g_error_free (error); + } + + g_object_unref (connection); + } + else + { + g_warning ("Cannot connect to session bus when initializing document portal: %s", + error->message); + g_error_free (error); + } + + g_once_init_leave (&openuri_inited, 1); + } + + return openuri != NULL; +} + +gboolean +g_openuri_portal_open_uri (const char *uri, + const char *parent_window, + GError **error) +{ + GFile *file = NULL; + GVariantBuilder opt_builder; + gboolean res; + + if (!init_openuri_portal ()) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "OpenURI portal is not available"); + return FALSE; + } + + g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); + + file = g_file_new_for_uri (uri); + if (g_file_is_native (file)) + { + char *path; + GUnixFDList *fd_list; + int fd, fd_id; + + path = g_file_get_path (file); + + fd = open (path, O_PATH | O_CLOEXEC); +#ifndef HAVE_O_CLOEXEC + fcntl (fd, F_SETFD, FD_CLOEXEC); +#endif + fd_list = g_unix_fd_list_new_from_array (&fd, 1); + fd_id = 0; + + res = gxdp_open_uri_call_open_file_sync (openuri, + parent_window ? parent_window : "", + g_variant_new ("h", fd_id), + g_variant_builder_end (&opt_builder), + fd_list, + NULL, + NULL, + NULL, + error); + g_free (path); + g_object_unref (fd_list); + } + else + { + res = gxdp_open_uri_call_open_uri_sync (openuri, + parent_window ? parent_window : "", + uri, + g_variant_builder_end (&opt_builder), + NULL, + NULL, + error); + } + + g_object_unref (file); + + return res; +} + +static void +response_received (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GTask *task = user_data; + guint32 response; + guint signal_id; + + signal_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task), "signal-id")); + g_dbus_connection_signal_unsubscribe (connection, signal_id); + + g_variant_get (parameters, "(u@a{sv})", &response, NULL); + + if (response == 0) + g_task_return_boolean (task, TRUE); + else if (response == 1) + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Launch cancelled"); + else + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Launch failed"); + + g_object_unref (task); +} + +static void +open_call_done (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (source); + GTask *task = user_data; + GError *error = NULL; + gboolean open_file; + gboolean res; + char *path; + guint signal_id; + + open_file = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "open-file")); + + if (open_file) + res = gxdp_open_uri_call_open_file_finish (openuri, &path, NULL, result, &error); + else + res = gxdp_open_uri_call_open_uri_finish (openuri, &path, result, &error); + + if (!res) + { + g_task_return_error (task, error); + g_object_unref (task); + g_free (path); + return; + } + + signal_id = g_dbus_connection_signal_subscribe (connection, + "org.freedesktop.portal.Desktop", + "org.freedesktop.portal.Request", + "Response", + path, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + response_received, + task, + NULL); + g_object_set_data (G_OBJECT (task), "signal-id", GINT_TO_POINTER (signal_id)); +} + +void +g_openuri_portal_open_uri_async (const char *uri, + const char *parent_window, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + GFile *file; + GVariantBuilder opt_builder; + + if (!init_openuri_portal ()) + { + GError *error = NULL; + + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "OpenURI portal is not available"); + g_task_report_error (NULL, callback, user_data, NULL, error); + return; + } + + if (callback) + task = g_task_new (NULL, cancellable, callback, user_data); + else + task = NULL; + + g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); + + file = g_file_new_for_uri (uri); + if (g_file_is_native (file)) + { + char *path; + GUnixFDList *fd_list; + int fd, fd_id; + + if (task) + g_object_set_data (G_OBJECT (task), "open-file", GINT_TO_POINTER (TRUE)); + + path = g_file_get_path (file); + fd = open (path, O_PATH | O_CLOEXEC); +#ifndef HAVE_O_CLOEXEC + fcntl (fd, F_SETFD, FD_CLOEXEC); +#endif + fd_list = g_unix_fd_list_new_from_array (&fd, 1); + fd_id = 0; + + gxdp_open_uri_call_open_file (openuri, + parent_window ? parent_window : "", + g_variant_new ("h", fd_id), + g_variant_builder_end (&opt_builder), + fd_list, + cancellable, + task ? open_call_done : NULL, + task); + g_object_unref (fd_list); + g_free (path); + } + else + { + gxdp_open_uri_call_open_uri (openuri, + parent_window ? parent_window : "", + uri, + g_variant_builder_end (&opt_builder), + cancellable, + task ? open_call_done : NULL, + task); + } + + g_object_unref (file); +} + +gboolean +g_openuri_portal_open_uri_finish (GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} diff --git a/gio/gopenuriportal.h b/gio/gopenuriportal.h new file mode 100644 index 000000000..49403b424 --- /dev/null +++ b/gio/gopenuriportal.h @@ -0,0 +1,41 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2017 Red Hat, Inc. + * + * This library 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __G_OPEN_URI_PORTAL_H__ + +#include <glib.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +gboolean g_openuri_portal_open_uri (const char *uri, + const char *parent_window, + GError **error); + +void g_openuri_portal_open_uri_async (const char *uri, + const char *parent_window, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean g_openuri_portal_open_uri_finish (GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif diff --git a/gio/org.freedesktop.portal.OpenURI.xml b/gio/org.freedesktop.portal.OpenURI.xml new file mode 100644 index 000000000..198d3c2df --- /dev/null +++ b/gio/org.freedesktop.portal.OpenURI.xml @@ -0,0 +1,105 @@ +<?xml version="1.0"?> +<!-- + Copyright (C) 2016 Red Hat, Inc. + + This library 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 <http://www.gnu.org/licenses/>. + + Author: Matthias Clasen <mclasen@redhat.com> +--> + +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <!-- + org.freedesktop.portal.OpenURI: + @short_description: Portal for opening URIs + + The OpenURI portal allows sandboxed applications to open + URIs (e.g. a http: link to the applications homepage) + under the control of the user. + --> + <interface name="org.freedesktop.portal.OpenURI"> + <!-- + OpenURI: + @parent_window: Identifier for the application window + @uri: The uri to open + @options: Vardict with optional further onformation + @handle: Object path for the #org.freedesktop.portal.Request object representing this call + + Asks to open a uri. + + The @parent_window identifier must be of the form "x11:$XID" for an X11 + window. Support for other window systems may be added in the future. + + Note that file:// uris are explicitly not supported by this method. + To request opening local files, use org.freedesktop.portal.OpenFile(). + + Supported keys in the @options vardict include: + <variablelist> + <varlistentry> + <term>writable b</term> + <listitem><para> + Whether to allow the chosen application to write to the file. + </para><para> + This key only takes effect the uri points to a local file that + is exported in the document portal, and the chosen application + is sandboxed itself. + </para></listitem> + </varlistentry> + </variablelist> + + --> + <method name="OpenURI"> + <arg type="s" name="parent_window" direction="in"/> + <arg type="s" name="uri" direction="in"/> + <arg type="a{sv}" name="options" direction="in"/> + <arg type="o" name="handle" direction="out"/> + </method> + + <!-- + OpenFile: + @parent_window: Identifier for the application window + @fd: File descriptor for the file to open + @options: Vardict with optional further onformation + @handle: Object path for the #org.freedesktop.portal.Request object representing this call + + Asks to open a local file. + + The @parent_window identifier must be of the form "x11:$XID" for an X11 + window. Support for other window systems may be added in the future. + + Supported keys in the @options vardict include: + <variablelist> + <varlistentry> + <term>writable b</term> + <listitem><para> + Whether to allow the chosen application to write to the file. + </para><para> + This key only takes effect the uri points to a local file that + is exported in the document portal, and the chosen application + is sandboxed itself. + </para></listitem> + </varlistentry> + </variablelist> + + The OpenFile method was introduced in version 2 of the OpenURI portal API. + --> + <method name="OpenFile"> + <arg type="s" name="parent_window" direction="in"/> + <arg type="h" name="fd" direction="in"/> + <arg type="a{sv}" name="options" direction="in"/> + <arg type="o" name="handle" direction="out"/> + </method> + + <property name="version" type="u" access="read"/> + </interface> +</node> |