summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2017-05-20 16:21:14 -0400
committerMatthias Clasen <mclasen@redhat.com>2017-05-26 18:21:32 -0400
commit0bc386c4cba383e4afb4baa11ac6802dd797caf8 (patch)
treea550a7ca8230f0f218ebf77781dbe5247fae1fcd
parentb5e8e4eea95aa429897ecb8a931d8985edb8b4c2 (diff)
downloadglib-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.am4
-rw-r--r--gio/gappinfo.c264
-rw-r--r--gio/gopenuriportal.c287
-rw-r--r--gio/gopenuriportal.h41
-rw-r--r--gio/org.freedesktop.portal.OpenURI.xml105
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>