[Spice-commits] 6 commits - meson.build src/map-file src/spice-glib-sym-file src/spice-gtk-session.c src/spice-session.c src/spice-session.h src/spice-session-priv.h

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Sep 9 13:50:44 UTC 2020


 meson.build              |   13 -
 src/map-file             |    1 
 src/spice-glib-sym-file  |    1 
 src/spice-gtk-session.c  |  399 +++++++++++++++++++++++++++++++++++++++++++++++
 src/spice-session-priv.h |    1 
 src/spice-session.c      |   30 +++
 src/spice-session.h      |    5 
 7 files changed, 443 insertions(+), 7 deletions(-)

New commits:
commit f33d589d747f4f7ee6a1241c344ca611a36e9c71
Author: Jakub Janků <jjanku at redhat.com>
Date:   Fri May 29 17:59:38 2020 +0200

    clipboard: enable copying files to guest using webdav
    
    When an app advertises the "text/uri-list" target, the user
    probably wants to copy/move files. Spice-gtk then sends
    a grab message to the vdagent advertising the
    VD_AGENT_CLIPBOARD_FILE_LIST type.
    
    Vdagent can then request clipboard data in this type.
    
    Spice-gtk tries to talk to the app that owns the clipboard
    in its native format in order to determine the preferred
    file operation (copy X move).
    
    For GNOME Nautilus, that's simply "UTF8_TEXT",
    for KDE Dolphin, "application/x-kde-cutselection".
    
    Otherwise the generic "text/uri-list" is used that does not
    provide any additional information.
    
    Once the uri list is obtained from the app, spice-gtk
    creates a unique virtual dir in the ".spice-clipboard"
    directory that is designated for this purpose.
    
    Each file is attached inside this virtual dir using
    phodav_virtual_dir_attach_real_child(), see phodav API
    for details.
    
    A list of paths in the phodav server is then sent to vdagent,
    as specified in the spice-protocol.
    Such path can for example look like this:
        /.spice-clipboard/b8f0249c-082a-4da9-9a38-2de3237a66f0/file
    
    It is up to the vdagent to ensure that the spice shared folder
    is accessible and to set the clipboard data in a format that
    other apps understand.
    
    This requires new phodav with PhodavVirtualDir API.
    
    Signed-off-by: Jakub Janků <jjanku at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/meson.build b/meson.build
index 7ade460..e43139e 100644
--- a/meson.build
+++ b/meson.build
@@ -33,7 +33,7 @@ spice_glib_deps = []
 spice_gtk_deps = []
 spice_wayland_deps = []
 spice_acl_deps = []
-spice_protocol_version = '0.14.2'
+spice_protocol_version = '0.14.3'
 
 #
 # Set up subprojects
diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c
index 5e6be4a..dfbd8fa 100644
--- a/src/spice-gtk-session.c
+++ b/src/spice-gtk-session.c
@@ -34,6 +34,10 @@
 #endif
 #endif
 
+#ifdef HAVE_PHODAV_VIRTUAL
+#include <libphodav/phodav.h>
+#endif
+
 #include <gtk/gtk.h>
 #include <spice/vd_agent.h>
 #include "desktop-integration.h"
@@ -61,6 +65,8 @@ struct _SpiceGtkSessionPrivate {
     gboolean                clip_grabbed[CLIPBOARD_LAST];
     gboolean                clipboard_by_guest[CLIPBOARD_LAST];
     guint                   clipboard_release_delay[CLIPBOARD_LAST];
+    /* TODO: maybe add a way of restoring this? */
+    GHashTable              *cb_shared_files;
     /* auto-usbredir related */
     gboolean                auto_usbredir_enable;
     int                     auto_usbredir_reqs;
@@ -191,6 +197,12 @@ static void spice_gtk_session_init(SpiceGtkSession *self)
 
     s = self->priv = spice_gtk_session_get_instance_private(self);
 
+    s->cb_shared_files =
+        g_hash_table_new_full(g_file_hash,
+                              (GEqualFunc)g_file_equal,
+                              g_object_unref, /* unref GFile */
+                              g_free /* free gchar * */
+                             );
     s->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
     g_signal_connect(G_OBJECT(s->clipboard), "owner-change",
                      G_CALLBACK(clipboard_owner_change), self);
@@ -252,6 +264,7 @@ static void spice_gtk_session_dispose(GObject *gobject)
                                              self);
         s->session = NULL;
     }
+    g_clear_pointer(&s->cb_shared_files, g_hash_table_destroy);
 
     /* Chain up to the parent class */
     if (G_OBJECT_CLASS(spice_gtk_session_parent_class)->dispose)
@@ -544,6 +557,9 @@ static const struct {
     },{
         .vdagent = VD_AGENT_CLIPBOARD_IMAGE_JPG,
         .xatom   = "image/jpeg"
+    },{
+        .vdagent = VD_AGENT_CLIPBOARD_FILE_LIST,
+        .xatom   = "text/uri-list"
     }
 };
 
@@ -660,6 +676,18 @@ static void clipboard_get_targets(GtkClipboard *clipboard,
                 continue;
             }
 
+            if (atom2agent[m].vdagent == VD_AGENT_CLIPBOARD_FILE_LIST) {
+#ifdef HAVE_PHODAV_VIRTUAL
+                if (!clipboard_get_open_webdav(s->session)) {
+                    SPICE_DEBUG("Received %s target, but the clipboard webdav channel "
+                                "isn't available, skipping", atom2agent[m].xatom);
+                    break;
+                }
+#else
+                break;
+#endif
+            }
+
             /* check if type is already in list */
             for (t = 0; t < num_types; t++) {
                 if (types[t] == atom2agent[m].vdagent) {
@@ -1037,6 +1065,318 @@ notify_agent:
     g_free(conv);
 }
 
+#ifdef HAVE_PHODAV_VIRTUAL
+/* returns path to @file under @root in clipboard phodav server, or NULL on error */
+static gchar *clipboard_webdav_share_file(PhodavVirtualDir *root, GFile *file)
+{
+    gchar *uuid;
+    PhodavVirtualDir *dir;
+    GError *err = NULL;
+
+    /* separate directory is created for each file,
+     * as we want to preserve the original filename and avoid conflicts */
+    for (guint i = 0; i < 8; i++) {
+        uuid = g_uuid_string_random();
+        gchar *dir_path = g_strdup_printf(SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH "/%s", uuid);
+        dir = phodav_virtual_dir_new_dir(root, dir_path, &err);
+        g_free(dir_path);
+        if (!err) {
+            break;
+        }
+        g_clear_pointer(&uuid, g_free);
+        if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+            g_warning("failed to create phodav virtual dir: %s", err->message);
+            g_error_free(err);
+            return NULL;
+        }
+        g_clear_error(&err);
+    }
+
+    if (!dir) {
+        g_warning("failed to create phodav virtual dir: all attempts failed");
+        return NULL;
+    }
+
+    phodav_virtual_dir_attach_real_child(dir, file);
+    g_object_unref(dir);
+
+    gchar *base = g_file_get_basename(file);
+    gchar *path = g_strdup_printf(SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH "/%s/%s", uuid, base);
+    g_free(uuid);
+    g_free(base);
+
+    return path;
+}
+
+/* join all strings in @strv into a new char array,
+ * including all terminating NULL-chars */
+static gchar *strv_concat(gchar **strv, gsize *size_out)
+{
+    gchar **str_p, *arr, *curr;
+
+    g_return_val_if_fail(strv && size_out, NULL);
+
+    for (str_p = strv, *size_out = 0; *str_p != NULL; str_p++) {
+        *size_out += strlen(*str_p) + 1;
+    }
+
+    arr = g_malloc(*size_out);
+
+    for (str_p = strv, curr = arr; *str_p != NULL; str_p++) {
+        curr = g_stpcpy(curr, *str_p) + 1;
+    }
+
+    return arr;
+}
+
+/* if not done alreay, share all files in @uris using the webdav server
+ * and return a new buffer with VD_AGENT_CLIPBOARD_FILE_LIST data */
+static gchar *strv_uris_transform_to_data(SpiceGtkSessionPrivate *s,
+    gchar **uris, gsize *size_out, GdkDragAction action)
+{
+    SpiceWebdavChannel *webdav;
+    PhodavServer *phodav;
+    PhodavVirtualDir *root;
+
+    gchar **uri_ptr, *path, **paths, *data;
+    GFile *file;
+    guint n;
+
+    *size_out = 0;
+
+    if (!uris || g_strv_length(uris) < 1) {
+        return NULL;
+    }
+
+    webdav = clipboard_get_open_webdav(s->session);
+    if (!webdav) {
+        SPICE_DEBUG("Received uris, but no webdav channel");
+        return NULL;
+    }
+
+    phodav = spice_session_get_webdav_server(s->session);
+    g_object_get(phodav, "root-file", &root, NULL);
+
+    paths = g_new0(gchar *, g_strv_length(uris) + 2);
+
+    paths[0] = action == GDK_ACTION_MOVE ? "cut" : "copy";
+    n = 1;
+
+    for (uri_ptr = uris; *uri_ptr != NULL; uri_ptr++) {
+        file = g_file_new_for_uri(*uri_ptr);
+
+        /* clipboard data is usually requested multiple times for no obvious reasons
+         * (clipboar managers to blame?), we don't want to create multiple dirs for the same file */
+        path = g_hash_table_lookup(s->cb_shared_files, file);
+        if (path) {
+            SPICE_DEBUG("found %s with path %s", *uri_ptr, path);
+            g_object_unref(file);
+        } else {
+            path = clipboard_webdav_share_file(root, file);
+            g_return_val_if_fail(path != NULL, NULL);
+            SPICE_DEBUG("publishing %s under %s", *uri_ptr, path);
+            /* file and path gets freed once the hash table gets destroyed */
+            g_hash_table_insert(s->cb_shared_files, file, path);
+        }
+        paths[n] = path;
+        n++;
+    }
+
+    g_object_unref(root);
+    data = strv_concat(paths, size_out);
+    g_free(paths);
+
+    return data;
+}
+
+static GdkAtom a_gnome, a_mate, a_nautilus, a_uri_list, a_kde_cut;
+
+static void init_uris_atoms()
+{
+    if (a_gnome != GDK_NONE) {
+        return;
+    }
+    a_gnome = gdk_atom_intern_static_string("x-special/gnome-copied-files");
+    a_mate = gdk_atom_intern_static_string("x-special/mate-copied-files");
+    a_nautilus = gdk_atom_intern_static_string("UTF8_STRING");
+    a_uri_list = gdk_atom_intern_static_string("text/uri-list");
+    a_kde_cut = gdk_atom_intern_static_string("application/x-kde-cutselection");
+}
+
+static GdkAtom clipboard_select_uris_atom(SpiceGtkSessionPrivate *s, guint selection)
+{
+    init_uris_atoms();
+    if (clipboard_find_atom(s, selection, a_gnome)) {
+        return a_gnome;
+    }
+    if (clipboard_find_atom(s, selection, a_mate)) {
+        return a_mate;
+    }
+    if (clipboard_find_atom(s, selection, a_nautilus)) {
+        return a_nautilus;
+    }
+    return clipboard_find_atom(s, selection, a_uri_list);
+}
+
+/* common handler for "x-special/gnome-copied-files" and "x-special/mate-copied-files" */
+static gchar *x_special_copied_files_transform_to_data(SpiceGtkSessionPrivate *s,
+    GtkSelectionData *selection_data, gsize *size_out)
+{
+    const gchar *text;
+    gchar **lines, *data = NULL;
+    GdkDragAction action;
+
+    *size_out = 0;
+
+    text = (gchar *)gtk_selection_data_get_data(selection_data);
+    if (!text) {
+        return NULL;
+    }
+    lines = g_strsplit(text, "\n", -1);
+    if (g_strv_length(lines) < 2) {
+        goto err;
+    }
+
+    if (!g_strcmp0(lines[0], "cut")) {
+        action = GDK_ACTION_MOVE;
+    } else if (!g_strcmp0(lines[0], "copy")) {
+        action = GDK_ACTION_COPY;
+    } else {
+        goto err;
+    }
+
+    data = strv_uris_transform_to_data(s, &lines[1], size_out, action);
+err:
+    g_strfreev(lines);
+    return data;
+}
+
+/* used with newer Nautilus */
+static gchar *nautilus_uris_transform_to_data(SpiceGtkSessionPrivate *s,
+    GtkSelectionData *selection_data, gsize *size_out, gboolean *retry_out)
+{
+    gchar **lines, *text, *data = NULL;
+    guint n_lines;
+    GdkDragAction action;
+
+    *size_out = 0;
+
+    text = (gchar *)gtk_selection_data_get_text(selection_data);
+    if (!text) {
+        return NULL;
+    }
+    lines = g_strsplit(text, "\n", -1);
+    g_free(text);
+    n_lines = g_strv_length(lines);
+
+    if (n_lines < 4) {
+        *retry_out = TRUE;
+        goto err;
+    }
+
+    if (g_strcmp0(lines[0], "x-special/nautilus-clipboard")) {
+        *retry_out = TRUE;
+        goto err;
+    }
+
+    if (!g_strcmp0(lines[1], "cut")) {
+        action = GDK_ACTION_MOVE;
+    } else if (!g_strcmp0(lines[1], "copy")) {
+        action = GDK_ACTION_COPY;
+    } else {
+        goto err;
+    }
+
+    /* the list of uris must end with \n,
+     * so there must be an empty string after the split */
+    if (g_strcmp0(lines[n_lines-1], "")) {
+        goto err;
+    }
+    g_clear_pointer(&lines[n_lines-1], g_free);
+
+    data = strv_uris_transform_to_data(s, &lines[2], size_out, action);
+err:
+    g_strfreev(lines);
+    return data;
+}
+
+static GdkDragAction kde_get_clipboard_action(SpiceGtkSessionPrivate *s, GtkClipboard *clipboard)
+{
+    GtkSelectionData *selection_data;
+    GdkDragAction action;
+    const guchar *data;
+
+    /* this uses another GMainLoop, basically the same mechanism
+     * as we use in clipboard_get(), so it doesn't block */
+    selection_data = gtk_clipboard_wait_for_contents(clipboard, a_kde_cut);
+    data = gtk_selection_data_get_data(selection_data);
+    if (data && data[0] == '1') {
+        action = GDK_ACTION_MOVE;
+    } else {
+        action = GDK_ACTION_COPY;
+    }
+    gtk_selection_data_free(selection_data);
+
+    return action;
+}
+
+static void clipboard_received_uri_contents_cb(GtkClipboard *clipboard,
+                                               GtkSelectionData *selection_data,
+                                               gpointer user_data)
+{
+    SpiceGtkSession *self = free_weak_ref(user_data);
+    SpiceGtkSessionPrivate *s;
+    guint selection;
+
+    if (!self) {
+        return;
+    }
+    s = self->priv;
+
+    selection = get_selection_from_clipboard(s, clipboard);
+    g_return_if_fail(selection != -1);
+
+    init_uris_atoms();
+    GdkAtom type = gtk_selection_data_get_data_type(selection_data);
+    gchar *data;
+    gsize len;
+
+    if (type == a_gnome || type == a_mate) {
+        /* used by old Nautilus + many other file managers  */
+        data = x_special_copied_files_transform_to_data(s, selection_data, &len);
+    } else if (type == a_nautilus) {
+        gboolean retry = FALSE;
+        data = nautilus_uris_transform_to_data(s, selection_data, &len, &retry);
+
+        if (retry && clipboard_find_atom(s, selection, a_uri_list) != GDK_NONE) {
+            /* it's not Nautilus, so we give it one more try with the generic uri-list target */
+            gtk_clipboard_request_contents(clipboard, a_uri_list,
+                clipboard_received_uri_contents_cb, get_weak_ref(self));
+            return;
+        }
+    } else if (type == a_uri_list) {
+        GdkDragAction action = GDK_ACTION_COPY;
+        gchar **uris = gtk_selection_data_get_uris(selection_data);
+
+        /* KDE uses a separate atom to distinguish between copy and move operation */
+        if (clipboard_find_atom(s, selection, a_kde_cut) != GDK_NONE) {
+            action = kde_get_clipboard_action(s, clipboard);
+        }
+
+        data = strv_uris_transform_to_data(s, uris, &len, action);
+        g_strfreev(uris);
+    } else {
+        g_warning("received uris in unsupported type");
+        data = NULL;
+        len = 0;
+    }
+
+    spice_main_channel_clipboard_selection_notify(s->main, selection,
+        VD_AGENT_CLIPBOARD_FILE_LIST, (guchar *)data, len);
+    g_free(data);
+}
+#endif
+
 static void clipboard_received_cb(GtkClipboard *clipboard,
                                   GtkSelectionData *selection_data,
                                   gpointer user_data)
@@ -1111,6 +1451,17 @@ static gboolean clipboard_request(SpiceMainChannel *main, guint selection,
     if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {
         gtk_clipboard_request_text(cb, clipboard_received_text_cb,
                                    get_weak_ref(self));
+    } else if (type == VD_AGENT_CLIPBOARD_FILE_LIST) {
+#ifdef HAVE_PHODAV_VIRTUAL
+        atom = clipboard_select_uris_atom(s, selection);
+        if (atom == GDK_NONE) {
+            return FALSE;
+        }
+        gtk_clipboard_request_contents(cb, atom,
+            clipboard_received_uri_contents_cb, get_weak_ref(self));
+#else
+        return FALSE;
+#endif
     } else {
         for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
             if (atom2agent[m].vdagent == type)
diff --git a/src/spice-session.c b/src/spice-session.c
index f0ac891..8831bc5 100644
--- a/src/spice-session.c
+++ b/src/spice-session.c
@@ -2666,6 +2666,17 @@ static void spice_session_set_shared_dir(SpiceSession *session, const gchar *dir
 
     g_free(s->shared_dir);
     s->shared_dir = g_strdup(dir);
+
+#ifdef HAVE_PHODAV_VIRTUAL
+    if (s->webdav == NULL) {
+        return;
+    }
+
+    PhodavVirtualDir *root;
+    g_object_get(s->webdav, "root-file", &root, NULL);
+    phodav_virtual_dir_root_set_real(root, s->shared_dir);
+    g_object_unref(root);
+#endif
 }
 
 G_GNUC_INTERNAL
@@ -2807,21 +2818,39 @@ PhodavServer* spice_session_get_webdav_server(SpiceSession *session)
     static GMutex mutex;
 
     const gchar *shared_dir = spice_session_get_shared_dir(session);
+    /* with HAVE_PHODAV_VIRTUAL, PhodavServer must be created even if shared_dir is NULL */
+#ifndef HAVE_PHODAV_VIRTUAL
     if (shared_dir == NULL) {
         SPICE_DEBUG("No shared dir set, not creating webdav server");
         return NULL;
     }
+#endif
 
     g_mutex_lock(&mutex);
 
     if (priv->webdav == NULL) {
+#ifdef HAVE_PHODAV_VIRTUAL
+        PhodavVirtualDir *root = phodav_virtual_dir_new_root();
+        priv->webdav = phodav_server_new_for_root_file(G_FILE(root));
+
+        phodav_virtual_dir_root_set_real(root, shared_dir);
+
+        g_object_unref(phodav_virtual_dir_new_dir(root, SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH, NULL));
+        g_object_unref(root);
+#else
         priv->webdav = phodav_server_new(shared_dir);
+#endif
+
         g_object_bind_property(session,  "share-dir-ro",
                                priv->webdav, "read-only",
                                G_BINDING_SYNC_CREATE|G_BINDING_BIDIRECTIONAL);
+
+        /* with HAVE_PHODAV_VIRTUAL, the update is done in spice_session_set_shared_dir() */
+#ifndef HAVE_PHODAV_VIRTUAL
         g_object_bind_property(session,  "shared-dir",
                                priv->webdav, "root",
                                G_BINDING_SYNC_CREATE|G_BINDING_BIDIRECTIONAL);
+#endif
     }
 
     g_mutex_unlock(&mutex);
diff --git a/src/spice-session.h b/src/spice-session.h
index 9436be8..665d2f3 100644
--- a/src/spice-session.h
+++ b/src/spice-session.h
@@ -36,6 +36,8 @@ G_BEGIN_DECLS
 #define SPICE_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_SESSION))
 #define SPICE_SESSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_SESSION, SpiceSessionClass))
 
+#define SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH "/.spice-clipboard"
+
 typedef struct _PhodavServer PhodavServer;
 
 /**
commit 996bfb48dc5d1d17bb56b4936f79d8705bcd5c48
Author: Jakub Janků <jjanku at redhat.com>
Date:   Tue Jun 30 15:15:39 2020 +0200

    spice-gtk-session: cache atoms
    
    At the moment, spice-gtk only sends a grab message to the vdagent
    based on the retrieved atoms.
    
    With the upcoming changes, spice-gtk will have to know which
    targets were advertised outside of clipboard_get_targets() callback.
    
    We could use gtk_clipboard_wait_for_targets() or
    gtk_clipboard_wait_is_*_available(), but the targets are not cached
    by GTK+ on wayland for some reason. So let's cache them in spice-gtk
    to avoid having to talk to the clipboard owner.
    
    Signed-off-by: Jakub Janků <jjanku at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c
index 2b86616..5e6be4a 100644
--- a/src/spice-gtk-session.c
+++ b/src/spice-gtk-session.c
@@ -55,6 +55,8 @@ struct _SpiceGtkSessionPrivate {
     GtkClipboard            *clipboard_primary;
     GtkTargetEntry          *clip_targets[CLIPBOARD_LAST];
     guint                   nclip_targets[CLIPBOARD_LAST];
+    GdkAtom                 *atoms[CLIPBOARD_LAST];
+    guint                   n_atoms[CLIPBOARD_LAST];
     gboolean                clip_hasdata[CLIPBOARD_LAST];
     gboolean                clip_grabbed[CLIPBOARD_LAST];
     gboolean                clipboard_by_guest[CLIPBOARD_LAST];
@@ -284,6 +286,8 @@ static void spice_gtk_session_finalize(GObject *gobject)
     for (i = 0; i < CLIPBOARD_LAST; ++i) {
         g_clear_pointer(&s->clip_targets[i], g_free);
         clipboard_release_delay_remove(self, i, true);
+        g_clear_pointer(&s->atoms[i], g_free);
+        s->n_atoms[i] = 0;
     }
 
     /* Chain up to the parent class */
@@ -589,6 +593,16 @@ static SpiceWebdavChannel *clipboard_get_open_webdav(SpiceSession *session)
     g_list_free(list);
     return open ? SPICE_WEBDAV_CHANNEL(channel) : NULL;
 }
+
+static GdkAtom clipboard_find_atom(SpiceGtkSessionPrivate *s, guint selection, GdkAtom a)
+{
+    for (int i = 0; i < s->n_atoms[selection]; i++) {
+        if (s->atoms[selection][i] == a) {
+            return a;
+        }
+    }
+    return GDK_NONE;
+}
 #endif
 
 static void clipboard_get_targets(GtkClipboard *clipboard,
@@ -622,6 +636,11 @@ static void clipboard_get_targets(GtkClipboard *clipboard,
     selection = get_selection_from_clipboard(s, clipboard);
     g_return_if_fail(selection != -1);
 
+    /* GTK+ does seem to cache atoms, but not for Wayland */
+    g_free(s->atoms[selection]);
+    s->atoms[selection] = g_memdup(atoms, n_atoms * sizeof(GdkAtom));
+    s->n_atoms[selection] = n_atoms;
+
     if (s->clip_grabbed[selection]) {
         SPICE_DEBUG("Clipboard is already grabbed, re-grab: %d atoms", n_atoms);
     }
@@ -705,6 +724,9 @@ static void clipboard_owner_change(GtkClipboard        *clipboard,
         return;
     }
 
+    g_clear_pointer(&s->atoms[selection], g_free);
+    s->n_atoms[selection] = 0;
+
     if (event->reason != GDK_OWNER_CHANGE_NEW_OWNER) {
         if (s->clip_grabbed[selection]) {
             /* grab was sent to the agent, so release it */
commit 852b847c868a199b5127644ca689f8a7d70fbda1
Author: Jakub Janků <jjanku at redhat.com>
Date:   Fri May 29 17:57:51 2020 +0200

    spice-gtk-session: add clipboard_get_open_webdav()
    
    File copy&paste functionality will only be enabled when there is an open
    webdav channel.
    
    Signed-off-by: Jakub Janků <jjanku at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c
index 48058c7..2b86616 100644
--- a/src/spice-gtk-session.c
+++ b/src/spice-gtk-session.c
@@ -565,6 +565,32 @@ static gpointer free_weak_ref(gpointer data)
     return object;
 }
 
+#ifdef HAVE_PHODAV_VIRTUAL
+static SpiceWebdavChannel *clipboard_get_open_webdav(SpiceSession *session)
+{
+    GList *list, *l;
+    SpiceChannel *channel = NULL;
+    gboolean open = FALSE;
+
+    g_return_val_if_fail(session != NULL, NULL);
+
+    list = spice_session_get_channels(session);
+    for (l = g_list_first(list); l != NULL; l = g_list_next(l)) {
+        channel = l->data;
+
+        if (!SPICE_IS_WEBDAV_CHANNEL(channel)) {
+            continue;
+        }
+
+        g_object_get(channel, "port-opened", &open, NULL);
+        break;
+    }
+
+    g_list_free(list);
+    return open ? SPICE_WEBDAV_CHANNEL(channel) : NULL;
+}
+#endif
+
 static void clipboard_get_targets(GtkClipboard *clipboard,
                                   GdkAtom *atoms,
                                   gint n_atoms,
commit c1b5433815e5cd7683671d33a0d579b7b185efe8
Author: Jakub Janků <jjanku at redhat.com>
Date:   Mon Jun 29 19:40:25 2020 +0200

    build: require GLib 2.52+
    
    This adds g_uuid_string_random()
    which is necessary for the following file copy&paste
    functionality.
    
    Signed-off-by: Jakub Janků <jjanku at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/meson.build b/meson.build
index 1c4e9d9..7ade460 100644
--- a/meson.build
+++ b/meson.build
@@ -89,7 +89,7 @@ endforeach
 #
 # check for mandatory dependencies
 #
-glib_version = '2.46'
+glib_version = '2.52'
 glib_version_info = '>= @0@'.format(glib_version)
 pixman_version = '>= 0.17.7'
 
commit 979b752b24d6f8d7089a23760fd5adda18f0e7ed
Author: Jakub Janků <jjanku at redhat.com>
Date:   Sat May 23 13:40:39 2020 +0200

    build: define HAVE_PHODAV_VIRTUAL if phodav >= 2.5
    
    Phodav 2.5 brings PhodavVirtualDir API needed for the
    file copy and paste functionality.
    
    If the library version is not sufficient, this new feature
    will be disabled, but the standard shared folders can still
    be used.
    
    Signed-off-by: Jakub Janků <jjanku at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/meson.build b/meson.build
index 6bbb4a8..1c4e9d9 100644
--- a/meson.build
+++ b/meson.build
@@ -177,14 +177,17 @@ endif
 
 # webdav
 spice_gtk_has_phodav = false
-d = dependency('libphodav-2.0', required: get_option('webdav'))
-if d.found()
-  spice_glib_deps += d
+phodav_dep = dependency('libphodav-2.0', required: get_option('webdav'))
+if phodav_dep.found()
+  spice_glib_deps += phodav_dep
   d = dependency('libsoup-2.4', version : '>= 2.49.91', required: get_option('webdav'))
   if d.found()
     spice_glib_deps += d
     spice_gtk_config_data.set('USE_PHODAV', '1')
     spice_gtk_has_phodav = true
+    if phodav_dep.version().version_compare('>= 2.5')
+      spice_gtk_config_data.set('HAVE_PHODAV_VIRTUAL', '1')
+    endif
   endif
 endif
 
commit 4b9092b96b8da946ff3d17922b0fcf225c5dc81f
Author: Jakub Janků <jjanku at redhat.com>
Date:   Sat May 23 16:28:52 2020 +0200

    session: make spice_session_get_webdav_server() public
    
    It will be necessary to access the webdav server from spice-gtk-session.c
    which isn't compiled with spice-session-priv.h, so make
    spice_session_get_webdav_server() public.
    
    Signed-off-by: Jakub Janků <jjanku at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/src/map-file b/src/map-file
index acdd38f..86f371d 100644
--- a/src/map-file
+++ b/src/map-file
@@ -144,6 +144,7 @@ spice_session_new;
 spice_session_open_fd;
 spice_session_verify_get_type;
 spice_set_session_option;
+spice_session_get_webdav_server;
 spice_smartcard_channel_get_type;
 spice_smartcard_manager_get;
 spice_smartcard_manager_get_readers;
diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file
index 72e6ef0..effcd09 100644
--- a/src/spice-glib-sym-file
+++ b/src/spice-glib-sym-file
@@ -123,6 +123,7 @@ spice_session_new
 spice_session_open_fd
 spice_session_verify_get_type
 spice_set_session_option
+spice_session_get_webdav_server
 spice_smartcard_channel_get_type
 spice_smartcard_manager_get
 spice_smartcard_manager_get_readers
diff --git a/src/spice-session-priv.h b/src/spice-session-priv.h
index b4919a4..5b52f8d 100644
--- a/src/spice-session-priv.h
+++ b/src/spice-session-priv.h
@@ -87,7 +87,6 @@ gboolean spice_session_get_smartcard_enabled(SpiceSession *session);
 gboolean spice_session_get_usbredir_enabled(SpiceSession *session);
 gboolean spice_session_get_gl_scanout_enabled(SpiceSession *session);
 
-PhodavServer *spice_session_get_webdav_server(SpiceSession *session);
 guint spice_session_get_n_display_channels(SpiceSession *session);
 gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession *mig_session);
 SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context);
diff --git a/src/spice-session.c b/src/spice-session.c
index 6915736..f0ac891 100644
--- a/src/spice-session.c
+++ b/src/spice-session.c
@@ -2796,7 +2796,6 @@ gboolean spice_session_get_smartcard_enabled(SpiceSession *session)
     return session->priv->smartcard;
 }
 
-G_GNUC_INTERNAL
 PhodavServer* spice_session_get_webdav_server(SpiceSession *session)
 {
     SpiceSessionPrivate *priv;
diff --git a/src/spice-session.h b/src/spice-session.h
index ed01c01..9436be8 100644
--- a/src/spice-session.h
+++ b/src/spice-session.h
@@ -36,6 +36,8 @@ G_BEGIN_DECLS
 #define SPICE_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_SESSION))
 #define SPICE_SESSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_SESSION, SpiceSessionClass))
 
+typedef struct _PhodavServer PhodavServer;
+
 /**
  * SpiceSessionVerify:
  * @SPICE_SESSION_VERIFY_PUBKEY: verify certificate public key matching
@@ -113,6 +115,7 @@ gboolean spice_session_has_channel_type(SpiceSession *session, gint type);
 gboolean spice_session_get_read_only(SpiceSession *session);
 SpiceURI *spice_session_get_proxy_uri(SpiceSession *session);
 gboolean spice_session_is_for_migration(SpiceSession *session);
+PhodavServer *spice_session_get_webdav_server(SpiceSession *session);
 
 G_END_DECLS
 


More information about the Spice-commits mailing list