summaryrefslogtreecommitdiff
path: root/gio/gopenuriportal.c
diff options
context:
space:
mode:
Diffstat (limited to 'gio/gopenuriportal.c')
-rw-r--r--gio/gopenuriportal.c287
1 files changed, 287 insertions, 0 deletions
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);
+}