In the past week I have written an X clipboard forwarder and hooked it up to Xpra. The forwarder is based on the XCB-based Python X library that I started last year with the intention of writing an X security proxy (http://plash.beasts.org/wiki/X11ProxySpike).
The novel part is that this is a trusted-path clipboard forwarder. Forwarding only occurs when the user presses Ctrl-X, Ctrl-C or Ctrl-V. The intention is to prevent malicious clients from snooping on the clipboard contents or writing data into the clipboard without action from the user. It does not forward X's primary selection, only the clipboard. The forwarder is only hooked up to the "xpra standalone" subcommand that is added by the last patch I sent, just because that was easiest to do quickly. For some background see http://lists.gnu.org/archive/html/plash/2008-06/msg00004.html and for a list of tasks still to do see http://plash.beasts.org/wiki/X11Selections This is just a request for comments at the moment. I don't expect this patch to be merged as is. For one thing, my X library does not have a stable interface yet, and it is debatable whether Xpra should depend on it. Regards, Mark diff --git a/xpra/client.py b/xpra/client.py index 8d0d467..9297cbc 100644 --- a/xpra/client.py +++ b/xpra/client.py @@ -48,7 +48,8 @@ class ClientSource(object): return None, False class ClientWindow(gtk.Window): - def __init__(self, client, id, x, y, w, h, metadata, override_redirect): + def __init__(self, client, id, x, y, w, h, metadata, override_redirect, + tp_clipboard): if override_redirect: type = gtk.WINDOW_POPUP else: @@ -61,6 +62,7 @@ class ClientWindow(gtk.Window): self._backing = None self._metadata = {} self._override_redirect = override_redirect + self._tp_clipboard = tp_clipboard self._new_backing(w, h) self.update_metadata(metadata) @@ -192,6 +194,11 @@ class ClientWindow(gtk.Window): def _key_action(self, event, depressed): modifiers = self._client.mask_to_names(event.state) name = gtk.gdk.keyval_name(event.keyval) + if modifiers == ["control"]: + if name in ("x", "c"): + self._tp_clipboard.copy() + elif name == "v": + self._tp_clipboard.paste() self._client.send(["key-action", self._id, name, depressed, modifiers]) def do_key_press_event(self, event): @@ -227,13 +234,24 @@ class ClientWindow(gtk.Window): gobject.type_register(ClientWindow) + +class DummyClipboard(object): + + def copy(self): + pass + + def paste(self): + pass + + class XpraClient(gobject.GObject): __gsignals__ = { "wimpiggy-property-notify-event": one_arg_signal, } - def __init__(self, sock): + def __init__(self, sock, tp_clipboard=DummyClipboard()): gobject.GObject.__init__(self) + self._tp_clipboard = tp_clipboard self._window_to_id = {} self._id_to_window = {} self._stacking = [] @@ -309,7 +327,7 @@ class XpraClient(gobject.GObject): def _process_new_common(self, packet, override_redirect): (_, id, x, y, w, h, metadata) = packet window = ClientWindow(self, id, x, y, w, h, metadata, - override_redirect) + override_redirect, self._tp_clipboard) self._id_to_window[id] = window self._window_to_id[window] = id window.show_all() diff --git a/xpra/scripts/main.py b/xpra/scripts/main.py index eded4f3..3cde24e 100644 --- a/xpra/scripts/main.py +++ b/xpra/scripts/main.py @@ -9,6 +9,10 @@ import tempfile from optparse import OptionParser import logging +import plash.comms.event_loop +import example_clients +import selection + import xpra from xpra.bencode import bencode from xpra.address import (sockdir, sockpath, @@ -176,7 +180,15 @@ def run_standalone(parser, args): client_socket.connect(socket_path) finally: shutil.rmtree(temp_dir) - client = xpra.client.XpraClient(client_socket) + + event_loop = plash.comms.event_loop.GlibEventLoop() + app_display = selection.SharableClientConnection( + example_clients.connect_to_display(event_loop, new_display)) + real_display = selection.SharableClientConnection( + example_clients.connect_to_default_display(event_loop)) + tp_clipboard = selection.AsymmetricForwarder(real_display, app_display) + + client = xpra.client.XpraClient(client_socket, tp_clipboard) environ = os.environ.copy() environ["DISPLAY"] = new_display proc = subprocess.Popen(args, env=environ) _______________________________________________ Parti-discuss mailing list [email protected] http://lists.partiwm.org/cgi-bin/mailman/listinfo/parti-discuss
