• Bug#1032450: marked as done (unblock: gtk+3.0/3.24.37-2) (2/3)

    From Debian Bug Tracking System@21:1/5 to All on Wed Mar 8 21:00:02 2023
    [continued from previous message]

    + fd_list,
    + NULL,
    + add_files_done, afd);
    +
    + g_object_unref (fd_list);
    +}
    +
    +static void
    +start_session_done (GObject *object,
    + GAsyncResult *result,
    + gpointer data)
    +{
    + GDBusProxy *proxy = G_DBUS_PROXY (object);
    + AddFileData *afd = data;
    + GError *error = NULL;
    + GVariant *ret;
    + const char *key;
    +
    + ret = g_dbus_proxy_call_finish (proxy, result, &error);
    + if (ret == NULL)
    + {
    + g_task_return_error (afd->task, error);
    + free_add_file_data (afd);
    + return;
    + }
    +
    + g_variant_get (ret, "(&s)", &key);
    +
    + g_object_set_data_full (G_OBJECT (afd->task), "key", g_strdup (key), g_free);
    +
    + g_variant_unref (ret);
    +
    + add_files (proxy, afd);
    +}
    +
    +void
    +file_transfer_portal_register_files (const char **files,
    + gboolean writable,
    + GAsyncReadyCallback callback,
    + gpointer data)
    +{
    + GTask *task;
    + AddFileData *afd;
    + GVariantBuilder options;
    +
    + task = g_task_new (NULL, NULL, callback, data);
    +
    + if (file_transfer_proxy == NULL)
    + {
    + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    + "No portal found");
    + g_object_unref (task);
    + return;
    + }
    +
    + afd = g_new (AddFileData, 1);
    + afd->task = task;
    + afd->files = g_strdupv ((char **)files);
    + afd->len = g_strv_length (afd->files);
    + afd->start = 0;
    +
    + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
    + g_variant_builder_add (&options, "{sv}", "writable", g_variant_new_boolean (writable));
    + g_variant_builder_add (&options, "{sv}", "autostop", g_variant_new_boolean (TRUE));
    +
    + g_dbus_proxy_call (file_transfer_proxy, "StartTransfer",
    + g_variant_new ("(a{sv})", &options),
    + 0, -1, NULL, start_session_done, afd);
    +}
    +
    +gboolean
    +file_transfer_portal_register_files_finish (GAsyncResult *result,
    + char **key,
    + GError **error)
    +{
    + if (g_task_propagate_boolean (G_TASK (result), error))
    + {
    + *key = g_strdup (g_object_get_data (G_OBJECT (result), "key"));
    + return TRUE;
    + }
    +
    + return FALSE;
    +}
    +
    +char *
    +file_transfer_portal_register_files_sync (const char **files,
    + gboolean writable,
    + GError **error)
    +{
    + const char *value;
    + char *key;
    + GUnixFDList *fd_list;
    + GVariantBuilder fds, options;
    + int i;
    + GVariant *ret;
    +
    + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
    + ret = g_dbus_proxy_call_sync (file_transfer_proxy,
    + "StartTransfer",
    + g_variant_new ("(a{sv})", &options),
    + 0,
    + -1,
    + NULL,
    + error);
    + if (ret == NULL)
    + return NULL;
    +
    + g_variant_get (ret, "(&s)", &value);
    + key = g_strdup (value);
    + g_variant_unref (ret);
    +
    + fd_list = NULL;
    +
    + for (i = 0; files[i]; i++)
    + {
    + int fd;
    + int fd_in;
    +
    + if (fd_list == NULL)
    + {
    + g_variant_builder_init (&fds, G_VARIANT_TYPE ("ah"));
    + fd_list = g_unix_fd_list_new ();
    + }
    +
    + fd = open (files[i], O_PATH | O_CLOEXEC);
    + if (fd == -1)
    + {
    + g_set_error (error,
    + G_IO_ERROR, g_io_error_from_errno (errno),
    + "Failed to open %s", files[i]);
    + g_variant_builder_clear (&fds);
    + g_object_unref (fd_list);
    + g_free (key);
    + return NULL;
    + }
    +
    +#ifndef HAVE_O_CLOEXEC
    + fcntl (fd, F_SETFD, FD_CLOEXEC);
    +#endif
    + fd_in = g_unix_fd_list_append (fd_list, fd, error);
    + close (fd);
    +
    + if (fd_in == -1)
    + {
    + g_variant_builder_clear (&fds);
    + g_object_unref (fd_list);
    + g_free (key);
    + return NULL;
    + }
    +
    + g_variant_builder_add (&fds, "h", fd_in);
    +
    + if ((i + 1) % 16 == 0 || files[i + 1] == NULL)
    + {
    + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
    + ret = g_dbus_proxy_call_with_unix_fd_list_sync (file_transfer_proxy, + "AddFiles",
    + g_variant_new ("(saha{sv})",
    + key, + &fds, + &options),
    + 0,
    + -1,
    + fd_list,
    + NULL,
    + NULL,
    + error);
    + g_clear_object (&fd_list);
    +
    + if (ret == NULL)
    + {
    + g_free (key);
    + return NULL;
    + }
    +
    + g_variant_unref (ret);
    + }
    + }
    +
    + return key;
    +}
    +
    +static void
    +retrieve_files_done (GObject *object,
    + GAsyncResult *result,
    + gpointer data)
    +{
    + GDBusProxy *proxy = G_DBUS_PROXY (object);
    + GTask *task = data;
    + GError *error = NULL;
    + GVariant *ret;
    + char **files;
    +
    + ret = g_dbus_proxy_call_finish (proxy, result, &error);
    + if (ret == NULL)
    + {
    + g_task_return_error (task, error);
    + g_object_unref (task);
    + return;
    + }
    +
    + g_variant_get (ret, "(^a&s)", &files);
    +
    + g_object_set_data_full (G_OBJECT (task), "files", g_strdupv (files), (GDestroyNotify)g_strfreev);
    +
    + g_variant_unref (ret);
    +
    + g_task_return_boolean (task, TRUE);
    +}
    +
    +void
    +file_transfer_portal_retrieve_files (const char *key,
    + GAsyncReadyCallback callback,
    + gpointer data)
    +{
    + GTask *task;
    + GVariantBuilder options;
    +
    + task = g_task_new (NULL, NULL, callback, data);
    +
    + if (file_transfer_proxy == NULL)
    + {
    + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    + "No portal found");
    + g_object_unref (task);
    + return;
    + }
    +
    + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
    + g_dbus_proxy_call (file_transfer_proxy,
    + "RetrieveFiles",
    + g_variant_new ("(sa{sv})", key, &options),
    + 0, -1, NULL,
    + retrieve_files_done, task);
    +}
    +
    +gboolean
    +file_transfer_portal_retrieve_files_finish (GAsyncResult *result,
    + char ***files,
    + GError **error)
    +{
    + if (g_task_propagate_boolean (G_TASK (result), error))
    + {
    + *files = g_strdupv (g_object_get_data (G_OBJECT (result), "files"));
    + return TRUE;
    + }
    +
    + return FALSE;
    +}
    +
    +char **
    +file_transfer_portal_retrieve_files_sync (const char *key,
    + GError **error)
    +{
    + GVariantBuilder options;
    + GVariant *ret;
    + char **files = NULL;
    +
    + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
    + ret = g_dbus_proxy_call_sync (file_transfer_proxy,
    + "RetrieveFiles",
    + g_variant_new ("(sa{sv})", key, &options),
    + 0, -1, NULL,
    + error);
    + if (ret)
    + {
    + const char **value;
    + g_variant_get (ret, "(^a&s)", &value);
    + files = g_strdupv ((char **)value);
    + g_variant_unref (ret);
    + }
    +
    + return files;
    +}
    +
    +static void
    +connection_closed (GDBusConnection *connection,
    + gboolean remote_peer_vanished,
    + GError *error)
    +{
    + g_clear_object (&file_transfer_proxy);
    +}
    +
    +static void
    +finish_registration (void)
    +{
    + /* Free the singleton when the connection closes, important for test */
    + g_signal_connect (g_dbus_proxy_get_connection (G_DBUS_PROXY (file_transfer_proxy)),
    + "closed", G_CALLBACK (connection_closed), NULL);
    +}
    +
    +static gboolean
    +proxy_has_owner (GDBusProxy *proxy)
    +{
    + char *owner;
    +
    + owner = g_dbus_proxy_get_name_owner (proxy);
    + if (owner)
    + {
    + g_free (owner);
    + return TRUE;
    + }
    +
    + return FALSE;
    +}
    +
    +void
    +file_transfer_portal_register (void)
    +{
    + static gboolean called;
    +
    + if (!called)
    + {
    + called = TRUE;
    +
    + file_transfer_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
    + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
    + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
    + NULL,
    + "org.freedesktop.portal.Documents",
    + "/org/freedesktop/portal/documents",
    + "org.freedesktop.portal.FileTransfer",
    + NULL,
    + NULL);
    +
    + if (file_transfer_proxy && !proxy_has_owner (file_transfer_proxy))
    + g_clear_object (&file_transfer_proxy);
    +
    + if (file_transfer_proxy)
    + finish_registration ();
    + }
    +}
    +
    +gboolean
    +file_transfer_portal_supported (void)
    +{
    + file_transfer_portal_register ();
    +
    + return file_transfer_proxy != NULL;
    +}
    +
    +#endif /* G_OS_UNIX */
    diff --git a/gtk/filetransferportalprivate.h b/gtk/filetransferportalprivate.h new file mode 100644
    index 0000000000..d136b53345
    --- /dev/null
    +++ b/gtk/filetransferportalprivate.h
    @@ -0,0 +1,49 @@
    +/*
    + * Copyright (C) 2018 Matthias Clasen
    + *
    + * 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 __FILE_TRANSFER_PROTOCOL_H__
    +#define __FILE_TRANSFER_PROTOCOL_H__
    +
    +
    +void file_transfer_portal_register (void);
    +
    +void file_transfer_portal_register_files (