Skip to content

Commit

Permalink
#452: clipboard loops:
Browse files Browse the repository at this point in the history
* detect loops at either end
* if at server end, send message to client (if it supports it)
* client syncs the tray menu entry state

git-svn-id: https://xpra.org/svn/Xpra/trunk@4847 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Dec 3, 2013
1 parent d0cffe0 commit 5c1c1e1
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 31 deletions.
11 changes: 11 additions & 0 deletions src/xpra/client/gtk2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ def get_tray_menu_helper_classes(self):
return tmhc


def process_clipboard_packet(self, packet):
log("process_clipboard_packet(%s) level=%s", packet, gtk.main_level())
#check for clipboard loops:
if gtk.main_level()>=10:
log.warn("loop nesting too deep: %s", gtk.main_level())
log.warn("you may have a clipboard forwarding loop, disabling the clipboard")
self.clipboard_enabled = False
self.emit("clipboard-toggled")
return
self.idle_add(self.clipboard_helper.process_clipboard_packet, packet)

def make_clipboard_helper(self):
"""
Try the various clipboard classes until we find one
Expand Down
9 changes: 7 additions & 2 deletions src/xpra/client/gtk_base/gtk_tray_menu_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,13 +377,13 @@ def set_notifications_menuitem(*args):
return self.notifications_menuitem

def make_clipboard_togglemenuitem(self):
def clipboard_toggled(*args):
def menu_clipboard_toggled(*args):
new_state = self.clipboard_menuitem.get_active()
debug("clipboard_toggled(%s) clipboard_enabled=%s, new_state=%s", args, self.client.clipboard_enabled, new_state)
if self.client.clipboard_enabled!=new_state:
self.client.clipboard_enabled = new_state
self.client.emit("clipboard-toggled")
self.clipboard_menuitem = self.checkitem("Clipboard", clipboard_toggled)
self.clipboard_menuitem = self.checkitem("Clipboard", menu_clipboard_toggled)
self.clipboard_menuitem.set_sensitive(False)
def set_clipboard_menuitem(*args):
self.clipboard_menuitem.set_active(self.client.clipboard_enabled)
Expand All @@ -395,6 +395,11 @@ def set_clipboard_menuitem(*args):
else:
set_tooltip_text(self.clipboard_menuitem, "Clipboard synchronization cannot be enabled: disabled by server")
self.client.connect("handshake-complete", set_clipboard_menuitem)
def clipboard_toggled(*args):
#keep menu in sync with actual "clipboard_enabled" flag:
if self.client.clipboard_enabled != self.clipboard_menuitem.get_active():
self.clipboard_menuitem.set_active(self.client.clipboard_enabled)
self.client.connect("clipboard-toggled", clipboard_toggled)
return self.clipboard_menuitem

def make_translatedclipboard_optionsmenuitem(self):
Expand Down
14 changes: 12 additions & 2 deletions src/xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ def __init__(self):
self.client_extras = None
self.keyboard_helper = None
self.clipboard_helper = None
self.clipboard_enabled = False
self.menu_helper = None
self.tray = None
self.notifier = None
Expand Down Expand Up @@ -645,6 +644,7 @@ def make_hello(self):
"clipboard.want_targets" : CLIPBOARD_WANT_TARGETS,
#buggy osx and win32 clipboards:
"clipboard.greedy" : CLIPBOARD_GREEDY,
"clipboard.set_enabled" : True,
"notifications" : self.client_supports_notifications,
"cursors" : self.client_supports_cursors,
"bell" : self.client_supports_bell,
Expand Down Expand Up @@ -1314,6 +1314,13 @@ def send_deflate_level(self):
self.send("set_deflate", self.compression_level)


def _process_clipboard_enabled_status(self, packet):
clipboard_enabled, reason = packet[1:3]
if self.clipboard_enabled!=clipboard_enabled:
log.info("clipboard toggled to %s by the server, reason: %s", clipboard_enabled, reason)
self.clipboard_enabled = clipboard_enabled
self.emit("clipboard-toggled")

def send_clipboard_enabled_status(self, *args):
self.send("set-clipboard-enabled", self.clipboard_enabled)

Expand Down Expand Up @@ -1589,6 +1596,7 @@ def init_packet_handlers(self):
"bell": self._process_bell,
"notify_show": self._process_notify_show,
"notify_close": self._process_notify_close,
"set-clipboard-enabled":self._process_clipboard_enabled_status,
"window-metadata": self._process_window_metadata,
"configure-override-redirect": self._process_configure_override_redirect,
"lost-window": self._process_lost_window,
Expand All @@ -1608,12 +1616,14 @@ def init_packet_handlers(self):
}.items():
self._packet_handlers[k] = v

def process_clipboard_packet(self, packet):
self.idle_add(self.clipboard_helper.process_clipboard_packet, packet)

def process_packet(self, proto, packet):
packet_type = packet[0]
self.check_server_echo(0)
if type(packet_type) in (unicode, str) and packet_type.startswith("clipboard-"):
if self.clipboard_enabled and self.clipboard_helper:
self.idle_add(self.clipboard_helper.process_clipboard_packet, packet)
self.process_clipboard_packet(packet)
else:
XpraClientBase.process_packet(self, proto, packet)
8 changes: 0 additions & 8 deletions src/xpra/clipboard/clipboard_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,14 +540,6 @@ def do_selection_get(self, selection_data, info, time):
assert self._selection == str(selection_data.selection)
target = str(selection_data.target)
self._request_contents_events += 1
#check for clipboard loops:
if gtk.main_level()>=20:
log.warn("clipboard loop nesting too deep: %s", gtk.main_level())
log.warn("you may have a clipboard forwarding loop, temporarily disabling the clipboard")
log.warn("it may be best to disable the clipboard completely")
self.set_enabled(False)
gobject.timeout_add(60*1000, self.set_enabled, True)
return
result = self.emit("get-clipboard-from-remote", self._selection, target)
if result is None or result["type"] is None:
debug("remote selection fetch timed out or empty")
Expand Down
26 changes: 26 additions & 0 deletions src/xpra/server/gtk_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,29 @@ def _process_move_window(self, proto, packet):

def _process_resize_window(self, proto, packet):
log.info("_process_resize_window(%s, %s)", proto, packet)


def send_clipboard_packet(self, *parts):
#overriden so we can inject the nesting check:
if self.clipboard_nesting_check(self._clipboard_client):
ServerBase.send_clipboard_packet(self, *parts)

def process_clipboard_packet(self, ss, packet):
#overriden so we can inject the nesting check:
if self.clipboard_nesting_check(ss):
ServerBase.process_clipboard_packet(self, ss, packet)

def clipboard_nesting_check(self, ss):
log("clipboard_nesting_check(%s)", ss)
if self._clipboard_client is None or not self._clipboard_client.clipboard_enabled:
return False
if gtk.main_level()>=10:
log.warn("loop nesting too deep: %s", gtk.main_level())
log.warn("you may have a clipboard forwarding loop, disabling the clipboard")
#turn off clipboard at our end:
self.set_clipboard_enabled_status(ss, False)
#if we can, tell the client to do the same:
if ss.clipboard_set_enabled:
ss.send_clipboard_enabled("probable clipboard loop detected")
return False
return True
46 changes: 27 additions & 19 deletions src/xpra/server/server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1044,13 +1044,17 @@ def _process_sound_data(self, proto, packet):

def _process_clipboard_enabled_status(self, proto, packet):
clipboard_enabled = packet[1]
if self._clipboard_helper:
assert self._clipboard_client==self._server_sources.get(proto), \
"the request to change the clipboard enabled status does not come from the clipboard owner!"
self._clipboard_client.clipboard_enabled = clipboard_enabled
log("toggled clipboard to %s", clipboard_enabled)
else:
ss = self._server_sources.get(proto)
self.set_clipboard_enabled_status(ss, clipboard_enabled)

def set_clipboard_enabled_status(self, ss, clipboard_enabled):
if not self._clipboard_helper:
log.warn("client toggled clipboard-enabled but we do not support clipboard at all! ignoring it")
return
assert self._clipboard_client==ss, \
"the request to change the clipboard enabled status does not come from the clipboard owner!"
self._clipboard_client.clipboard_enabled = clipboard_enabled
log("toggled clipboard to %s", clipboard_enabled)

def _process_keyboard_sync_enabled_status(self, proto, packet):
self.keyboard_sync = bool(packet[1])
Expand Down Expand Up @@ -1324,6 +1328,21 @@ def _process_resize_window(self, proto, packet):
log.info("_process_resize_window(%s, %s)", proto, packet)


def process_clipboard_packet(self, ss, packet):
if not ss:
#protocol has been dropped!
return
assert self._clipboard_client==ss, \
"the clipboard packet '%s' does not come from the clipboard owner!" % packet[0]
if not ss.clipboard_enabled:
#this can happen when we disable clipboard in the middle of transfers
#(especially when there is a clipboard loop)
log.warn("received a clipboard packet from a source which does not have clipboard enabled!")
return
assert self._clipboard_helper, "received a clipboard packet but we do not support clipboard sharing"
self.idle_add(self._clipboard_helper.process_clipboard_packet, packet)


def process_packet(self, proto, packet):
try:
handler = None
Expand All @@ -1332,20 +1351,9 @@ def process_packet(self, proto, packet):
packet_type = self._aliases.get(packet_type)
assert isinstance(packet_type, (str, unicode)), "packet_type %s is not a string: %s..." % (type(packet_type), str(packet_type)[:100])
if packet_type.startswith("clipboard-"):
handler = self.process_clipboard_packet
ss = self._server_sources.get(proto)
if not ss:
#protocol has been dropped!
return
handler = self._clipboard_helper.process_clipboard_packet
assert self._clipboard_client==ss, \
"the clipboard packet '%s' does not come from the clipboard owner!" % packet_type
if not ss.clipboard_enabled:
#this can happen when we disable clipboard in the middle of transfers
#(especially when there is a clipboard loop)
log.warn("received a clipboard packet from a source which does not have clipboard enabled!")
return
assert self._clipboard_helper, "received a clipboard packet but we do not support clipboard sharing"
self.idle_add(handler, packet)
self.process_clipboard_packet(ss, packet)
return
if proto in self._server_sources:
handlers = self._authenticated_packet_handlers
Expand Down
5 changes: 5 additions & 0 deletions src/xpra/server/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ def __init__(self, protocol, disconnect_cb, idle_add, timeout_add, source_remove
self.named_cursors = False
self.clipboard_enabled = False
self.clipboard_notifications = False
self.clipboard_set_enabled = False
self.share = False
self.desktop_size = None
self.screen_sizes = []
Expand Down Expand Up @@ -461,6 +462,7 @@ def parse_batch_int(value, varname):
self.randr_notify = c.boolget("randr_notify")
self.clipboard_enabled = c.boolget("clipboard", True)
self.clipboard_notifications = c.boolget("clipboard.notifications")
self.clipboard_set_enabled = c.boolget("clipboard.set_enabled")
self.share = c.boolget("share")
self.named_cursors = c.boolget("named_cursors")
self.raw_window_icons = c.boolget("raw_window_icons")
Expand Down Expand Up @@ -1023,6 +1025,9 @@ def rewrite_encoding_values(self, d):
debug("rewrite_encoding_values for key '%s': %s replaced by %s", k, l, newlist)


def send_clipboard_enabled(self, reason=""):
self.send("set-clipboard-enabled", self.clipboard_enabled, reason)

def send_clipboard(self, packet):
if not self.clipboard_enabled or self.suspended:
return
Expand Down

0 comments on commit 5c1c1e1

Please sign in to comment.