From b348b100d8d5458cd3f68660973e311d997929e1 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 11 Apr 2019 12:19:41 +0000 Subject: [PATCH] #812 native x11 clipboard improvements: * distinguish targets we never want to handle from the ones that we ignore when not explicitly exposed by the peer, * use tuples for lists of atoms * python3 string nonsense: convert all network metadata to strings, * make timeout values configurable via env vars, * filter atom targets in and out, * cache 'TARGETS' data so we can detect spurious target requests and drop them git-svn-id: https://xpra.org/svn/Xpra/trunk@22375 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/clipboard/clipboard_core.py | 56 +++++++++++++++------- src/xpra/x11/gtk_x11/clipboard.py | 70 +++++++++++++++++----------- 2 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/xpra/clipboard/clipboard_core.py b/src/xpra/clipboard/clipboard_core.py index 530cc63140..461a457886 100644 --- a/src/xpra/clipboard/clipboard_core.py +++ b/src/xpra/clipboard/clipboard_core.py @@ -38,21 +38,31 @@ LOOP_DISABLE = envbool("XPRA_CLIPBOARD_LOOP_DISABLE", True) LOOP_PREFIX = os.environ.get("XPRA_CLIPBOARD_LOOP_PREFIX", "Xpra-Clipboard-Loop-Detection:") -def get_discard_targets(): - _discard_target_strs_ = os.environ.get("XPRA_DISCARD_TARGETS") +def get_discard_targets(envname="DISCARD", default_value=()): + _discard_target_strs_ = os.environ.get("XPRA_%s_TARGETS" % envname) if _discard_target_strs_ is None: - return [ - r"^SAVE_TARGETS$", - r"^COMPOUND_TEXT", - r"^NeXT", - r"^com\.apple\.", - r"^CorePasteboardFlavorType", - r"^dyn\.", - r"^text/plain;charset=utf-8", - ] + return default_value return _discard_target_strs_.split(",") -DISCARD_TARGETS = tuple(re.compile(dt) for dt in get_discard_targets()) -log("discard_targets=%s", csv(get_discard_targets())) +#targets we never wish to handle: +DISCARD_TARGETS = tuple(re.compile(dt) for dt in get_discard_targets("DISCARD", ( + r"^NeXT", + r"^com\.apple\.", + r"^CorePasteboardFlavorType", + r"^dyn\.", + ))) +#targets some applications are known to request, +#even when the peer did not expose them as valid targets, +#rather forwarding the request and then timing out, +#we will just drop them +DISCARD_EXTRA_TARGETS = tuple(re.compile(dt) for dt in get_discard_targets("DISCARD_EXTRA", ( + r"^SAVE_TARGETS$", + r"^COMPOUND_TEXT", + r"GTK_TEXT_BUFFER_CONTENTS", + r"^text/plain;charset=utf-8", + ))) +log("DISCARD_TARGETS=%s", csv(DISCARD_TARGETS)) +log("DISCARD_EXTRA_TARGETS=%s", csv(DISCARD_EXTRA_TARGETS)) + TEXT_TARGETS = ("UTF8_STRING", "TEXT", "STRING", "text/plain") @@ -69,8 +79,12 @@ def get_discard_targets(): def must_discard(target): return any(x for x in DISCARD_TARGETS if x.match(bytestostr(target))) +def must_discard_extra(target): + return any(x for x in DISCARD_EXTRA_TARGETS if x.match(bytestostr(target))) + + def _filter_targets(targets): - f = [target for target in targets if not must_discard(target)] + f = tuple(target for target in targets if not must_discard(target)) log("_filter_targets(%s)=%s", targets, f) return f @@ -267,6 +281,9 @@ def _process_clipboard_token(self, packet): targets = packet[2] if len(packet)>=8: target, dtype, dformat, wire_encoding, wire_data = packet[3:8] + target = bytestostr(target) + wire_encoding = bytestostr(wire_encoding) + dtype = bytestostr(dtype) raw_data = self._munge_wire_selection_to_raw(wire_encoding, dtype, dformat, wire_data) target_data = {target : (dtype, dformat, raw_data)} #older versions always claimed the selection when the token is received: @@ -333,11 +350,11 @@ def got_contents(dtype, dformat, data): send_token(rsel) def _munge_raw_selection_to_wire(self, target, dtype, dformat, data): - log("_munge_raw_selection_to_wire%s", (target, dtype, dformat, data)) + log("_munge_raw_selection_to_wire%s", (target, dtype, dformat, repr_ellipsized(bytestostr(data)))) # Some types just cannot be marshalled: if type in ("WINDOW", "PIXMAP", "BITMAP", "DRAWABLE", "PIXEL", "COLORMAP"): - log("skipping clipboard data of type: %s, format=%s, len(data)=%s", dtype, dformat, len(data or "")) + log("skipping clipboard data of type: %s, format=%s, len(data)=%s", dtype, dformat, len(data or b"")) return None, None if target=="TARGETS" and dtype=="ATOM": #targets is special cased here @@ -389,9 +406,9 @@ def _munge_wire_selection_to_raw(self, encoding, dtype, dformat, data): olen = len(data) data = data[:max_recv_datalen] log.info("Data copied out truncated because of clipboard policy %d to %d", olen, max_recv_datalen) - if encoding == b"bytes": + if encoding == "bytes": return data - if encoding == b"integers": + if encoding == "integers": if not data: return "" if dformat == 32: @@ -441,6 +458,7 @@ def no_contents(): log.warn("clipboard request %s dropped for testing!", request_id) return def got_contents(dtype, dformat, data): + dtype = bytestostr(dtype) log("got_contents(%s, %s, %s:%s) data=0x%s..", dtype, dformat, type(data), len(data or ""), hexstr((data or "")[:200])) if dtype is None or data is None or (dformat==0 and data==b""): @@ -481,6 +499,8 @@ def _may_compress(self, dtype, dformat, wire_data): def _process_clipboard_contents(self, packet): request_id, selection, dtype, dformat, wire_encoding, wire_data = packet[1:7] log("process clipboard contents, selection=%s, type=%s, format=%s", selection, dtype, dformat) + wire_encoding = bytestostr(wire_encoding) + dtype = bytestostr(dtype) raw_data = self._munge_wire_selection_to_raw(wire_encoding, dtype, dformat, wire_data) log("clipboard wire -> raw: %r -> %r", (dtype, dformat, wire_encoding, wire_data), raw_data) self._clipboard_got_contents(request_id, dtype, dformat, raw_data) diff --git a/src/xpra/x11/gtk_x11/clipboard.py b/src/xpra/x11/gtk_x11/clipboard.py index 853922332f..b347852613 100644 --- a/src/xpra/x11/gtk_x11/clipboard.py +++ b/src/xpra/x11/gtk_x11/clipboard.py @@ -18,13 +18,14 @@ ) from xpra.clipboard.clipboard_core import ( ClipboardProtocolHelperCore, ClipboardProxyCore, - must_discard, + must_discard, must_discard_extra, _filter_targets, ) from xpra.x11.bindings.window_bindings import ( #@UnresolvedImport constants, PropertyError, X11WindowBindings, ) -from xpra.util import csv, repr_ellipsized +from xpra.os_util import bytestostr +from xpra.util import csv, repr_ellipsized, envint from xpra.log import Logger gdk = import_gdk() @@ -41,17 +42,23 @@ sizeof_long = struct.calcsize(b'@L') +CONVERT_TIMEOUT = envint("XPRA_CLIPBOARD_CONVERT_TIMEOUT", 500) +REMOTE_TIMEOUT = envint("XPRA_CLIPBOARD_REMOTE_TIMEOUT", 1500) +assert 0