예제 #1
0
파일: wrapper.py 프로젝트: svn2github/Xpra
def query_sound():
    import subprocess
    command = get_sound_command()+["_sound_query"]
    _add_debug_args(command)
    kwargs = exec_kwargs()
    env = exec_env()
    env.update(get_sound_wrapper_env())
    log("query_sound() command=%s, env=%s, kwargs=%s", command, env, kwargs)
    proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, env=env, **kwargs)
    out, err = proc.communicate(None)
    log("query_sound() process returned %s", proc.returncode)
    log("query_sound() out=%s, err=%s", out, err)
    if proc.returncode!=0:
        return typedict()
    d = typedict()
    for x in out.decode("utf8").splitlines():
        kv = x.split("=", 1)
        if len(kv)==2:
            #ie: kv = ["decoders", "mp3,vorbis"]
            k,v = kv
            #fugly warning: all the other values are lists.. but this one is not:
            if k!=b"python.bits":
                v = [x.encode() for x in v.split(",")]
            #d["decoders"] = ["mp3", "vorbis"]
            d[k.encode()] = v
    log("query_sound()=%s", d)
    return d
 def paint_and_vscroll(self, ydelta=10):
     print("paint_and_scroll(%i)" % (ydelta))
     W, H = self.window.get_size()
     if ydelta>0:
         #scroll down, repaint the top:
         scrolls = (0, 0, W, H-ydelta, 0, ydelta),
         self.window.draw_region(0, 0, W, H, "scroll", scrolls, W*4, 0, typedict({"flush" : 1}), [])
         self.paint_crect(0, 0, W, ydelta, 0x80808080)
     else:
         #scroll up, repaint the bottom:
         scrolls = (0, -ydelta, W, H+ydelta, 0, ydelta),
         self.window.draw_region(0, 0, W, H, "scroll", scrolls, W*4, 0, typedict({"flush" : 1}), [])
         self.paint_crect(0, H-ydelta, W, -ydelta, 0xA0008020)
예제 #3
0
    def __init__(self, client, group_leader, watcher_pid, wid, wx, wy, ww, wh, bw, bh, metadata, override_redirect, client_properties, border, max_window_size, default_cursor_data, pixel_depth):
        log("%s%s", type(self), (client, group_leader, watcher_pid, wid, wx, wy, ww, wh, bw, bh, metadata, override_redirect, client_properties, max_window_size, default_cursor_data, pixel_depth))
        ClientWidgetBase.__init__(self, client, watcher_pid, wid, metadata.boolget("has-alpha"))
        self._override_redirect = override_redirect
        self.group_leader = group_leader
        self._pos = (wx, wy)
        self._size = (ww, wh)
        self._client_properties = client_properties
        self._set_initial_position = metadata.boolget("set-initial-position", False)
        self.size_constraints = typedict()
        self.geometry_hints = {}
        self._fullscreen = None
        self._maximized = False
        self._above = False
        self._below = False
        self._shaded = False
        self._sticky = False
        self._skip_pager = False
        self._skip_taskbar = False
        self._sticky = False
        self._iconified = False
        self._focused = False
        self.border = border
        self.cursor_data = None
        self.default_cursor_data = default_cursor_data
        self.max_window_size = max_window_size
        self.button_state = {}
        self.pixel_depth = pixel_depth      #0 for default
        self.window_offset = None           #actual vs reported coordinates

        self.init_window(metadata)
        self.setup_window(bw, bh)
        self.update_metadata(metadata)
예제 #4
0
 def do_process_control_packet(self, proto, packet):
     log("process_control_packet(%s, %s)", proto, packet)
     packet_type = packet[0]
     if packet_type==Protocol.CONNECTION_LOST:
         log.info("Connection lost")
         if proto in self.potential_protocols:
             self.potential_protocols.remove(proto)
         return
     if packet_type=="hello":
         caps = typedict(packet[1])
         if caps.boolget("challenge"):
             self.send_disconnect(proto, AUTHENTICATION_ERROR, "this socket does not use authentication")
             return
         generic_request = caps.strget("request")
         def is_req(mode):
             return generic_request==mode or caps.boolget("%s_request" % mode)
         if is_req("info"):
             proto.send_now(("hello", self.get_proxy_info(proto)))
             self.timeout_add(5*1000, self.send_disconnect, proto, CLIENT_EXIT_TIMEOUT, "info sent")
             return
         elif is_req("stop"):
             self.stop("socket request", None)
             return
         elif is_req("version"):
             version = XPRA_VERSION
             if caps.boolget("full-version-request"):
                 version = full_version_str()
             proto.send_now(("hello", {"version" : version}))
             self.timeout_add(5*1000, self.send_disconnect, proto, CLIENT_EXIT_TIMEOUT, "version sent")
             return
     self.send_disconnect(proto, CONTROL_COMMAND_ERROR, "this socket only handles 'info', 'version' and 'stop' requests")
예제 #5
0
    def _process_hello(self, proto, packet):
        capabilities = packet[1]
        c = typedict(capabilities)
        proto.set_compression_level(c.intget("compression_level", self.compression_level))
        if use_rencode and c.boolget("rencode"):
            proto.enable_rencode()
        else:
            proto.enable_bencode()

        if c.boolget("lz4") and use_lz4 and self.compression_level==1:
            proto.enable_lz4()

        log("process_hello: capabilities=%s", capabilities)
        if c.boolget("version_request"):
            self.send_version_info(proto)
            return False

        auth_caps = self.verify_hello(proto, c)
        if auth_caps is not False:
            if c.boolget("info_request", False):
                self.send_hello_info(proto)
                return
            command_req = c.strlistget("command_request")
            if len(command_req)>0:
                #call from UI thread:
                self.idle_add(self.handle_command_request, proto, command_req)
                return
            #continue processing hello packet:
            try:
                self.hello_oked(proto, packet, c, auth_caps)
            except:
                log.error("server error processing new connection from %s", proto, exc_info=True)
                self.disconnect_client(proto, "server error accepting new connection")
예제 #6
0
 def process_network_packet(self, proto, packet):
     log("process_network_packet: %s", packet)
     packet_type = bytestostr(packet[0])
     def close():
         t = self._close_timers.pop(proto, None)
         if t:
             proto.close()
         try:
             self._potential_protocols.remove(proto)
         except ValueError:
             pass
     def hello_reply(data):
         proto.send_now(["hello", data])
     if packet_type=="hello":
         caps = typedict(packet[1])
         proto.parse_remote_caps(caps)
         proto.enable_compressor_from_caps(caps)
         proto.enable_encoder_from_caps(caps)
         request = caps.strget("request")
         if request=="info":
             def send_info():
                 info = self.get_info()
                 info["network"] = get_network_caps()
                 hello_reply(info)
             #run in UI thread:
             self.idle_add(send_info)
         elif request=="id":
             hello_reply(self.get_id_info())
         elif request=="detach":
             def protocol_closed():
                 self.disconnect_and_quit(EXIT_OK, "network request")
             proto.send_disconnect([DETACH_REQUEST], done_callback=protocol_closed)
             return
         elif request=="version":
             hello_reply({"version" : VERSION})
         elif request=="command":
             command = caps.strtupleget("command_request")
             log("command request: %s", command)
             def process_control():
                 try:
                     self._process_control(["control"]+list(command))
                     code = EXIT_OK
                     response = "done"
                 except Exception as e:
                     code = EXIT_FAILURE
                     response = str(e)
                 hello_reply({"command_response"  : (code, response)})
             self.idle_add(process_control)
         else:
             log.info("request '%s' is not handled by this client", request)
             proto.send_disconnect([PROTOCOL_ERROR])
     elif packet_type in (Protocol.CONNECTION_LOST, Protocol.GIBBERISH):
         close()
         return
     else:
         log.info("packet '%s' is not handled by this client", packet_type)
         proto.send_disconnect([PROTOCOL_ERROR])
     #make sure the connection is closed:
     tid = self.timeout_add(REQUEST_TIMEOUT*1000, close)
     self._close_timers[proto] = tid
예제 #7
0
 def do_process_control_packet(self, proto, packet):
     log("process_control_packet(%s, %s)", proto, packet)
     packet_type = packet[0]
     if packet_type==Protocol.CONNECTION_LOST:
         log.info("Connection lost")
         if proto in self.potential_protocols:
             self.potential_protocols.remove(proto)
         return
     if packet_type=="hello":
         caps = typedict(packet[1])
         if caps.boolget("challenge"):
             self.send_disconnect(proto, AUTHENTICATION_ERROR, "this socket does not use authentication")
             return
         if caps.get("info_request", False):
             proto.send_now(("hello", self.get_proxy_info(proto)))
             self.timeout_add(5*1000, self.send_disconnect, proto, CLIENT_EXIT_TIMEOUT, "info sent")
             return
         elif caps.get("stop_request", False):
             self.stop("socket request", None)
             return
         elif caps.get("version_request", False):
             from xpra import __version__
             proto.send_now(("hello", {"version" : __version__}))
             self.timeout_add(5*1000, self.send_disconnect, proto, CLIENT_EXIT_TIMEOUT, "version sent")
             return
     self.send_disconnect(proto, CONTROL_COMMAND_ERROR, "this socket only handles 'info', 'version' and 'stop' requests")
예제 #8
0
 def init_window(self, metadata):
     self._backing = None
     self._metadata = typedict()
     # used for only sending focus events *after* the window is mapped:
     self._been_mapped = False
     self._override_redirect_windows = []
     self.update_metadata(metadata)
예제 #9
0
    def parse_client_caps(self, c: typedict):
        #general features:
        self.info_namespace = c.boolget("info-namespace")
        self.share = c.boolget("share")
        self.lock = c.boolget("lock")
        self.control_commands = c.strtupleget("control_commands")
        self.xdg_menu_update = c.boolget("xdg-menu-update")
        bandwidth_limit = c.intget("bandwidth-limit", 0)
        server_bandwidth_limit = self.server_bandwidth_limit
        if self.server_bandwidth_limit is None:
            server_bandwidth_limit = self.get_socket_bandwidth_limit(
            ) or bandwidth_limit
        self.bandwidth_limit = min(server_bandwidth_limit, bandwidth_limit)
        if self.bandwidth_detection:
            self.bandwidth_detection = c.boolget("bandwidth-detection", True)
        self.client_connection_data = c.dictget("connection-data", {})
        ccd = typedict(self.client_connection_data)
        self.adapter_type = ccd.strget("adapter-type", "")
        self.jitter = ccd.intget("jitter", 0)
        bandwidthlog(
            "server bandwidth-limit=%s, client bandwidth-limit=%s, value=%s, detection=%s",
            server_bandwidth_limit, bandwidth_limit, self.bandwidth_limit,
            self.bandwidth_detection)

        if getattr(self, "mmap_size", 0) > 0:
            log("mmap enabled, ignoring bandwidth-limit")
            self.bandwidth_limit = 0
예제 #10
0
    def set_icc_profile(self):
        if not SYNC_ICC:
            return
        ui_clients = [s for s in self._server_sources.values() if s.ui_client]
        if len(ui_clients) != 1:
            screenlog("%i UI clients, resetting ICC profile to default",
                      len(ui_clients))
            self.reset_icc_profile()
            return
        icc = typedict(ui_clients[0].icc)
        data = None
        for x in ("data", "icc-data", "icc-profile"):
            data = icc.strget(x)
            if data:
                break
        if not data:
            screenlog("no icc data found in %s", icc)
            self.reset_icc_profile()
            return
        screenlog("set_icc_profile() icc data for %s: %s (%i bytes)",
                  ui_clients[0], hexstr(data or ""), len(data or ""))
        from xpra.x11.gtk_x11.prop import prop_set

        #each CARD32 contains just one 8-bit value - don't ask me why
        def o(x):
            try:
                return ord(x)
            except:
                return x

        prop_set(self.root_window, "_ICC_PROFILE", ["u32"],
                 [o(x) for x in data])
        prop_set(self.root_window, "_ICC_PROFILE_IN_X_VERSION", "u32",
                 0 * 100 + 4)  #0.4 -> 0*100+4*1
예제 #11
0
 def set_strut(self, strut):
     if not HAS_X11_BINDINGS:
         return
     log("strut=%s", strut)
     d = typedict(strut)
     values = []
     for x in ("left", "right", "top", "bottom"):
         v = d.intget(x, 0)
         #handle scaling:
         if x in ("left", "right"):
             v = self._client.sx(v)
         else:
             v = self._client.sy(v)
         values.append(v)
     has_partial = False
     for x in ("left_start_y", "left_end_y",
               "right_start_y", "right_end_y",
               "top_start_x", "top_end_x",
               "bottom_start_x", "bottom_end_x"):
         if x in d:
             has_partial = True
         v = d.intget(x, 0)
         if x.find("_x"):
             v = self._client.sx(v)
         elif x.find("_y"):
             v = self._client.sy(v)
         values.append(v)
     log("setting strut=%s, has partial=%s", values, has_partial)
     def do_set_strut():
         if has_partial:
             prop_set(self.get_window(), "_NET_WM_STRUT_PARTIAL", ["u32"], values)
         prop_set(self.get_window(), "_NET_WM_STRUT", ["u32"], values[:4])
     self.when_realized("strut", do_set_strut)
예제 #12
0
    def parse_client_caps(self, c):
        #self.ui_client = c.boolget("ui_client", True)
        self.send_windows = self.ui_client and c.boolget("windows", True)
        self.pointer_grabs = c.boolget("pointer.grabs")
        self.send_cursors = self.send_windows and c.boolget("cursors")
        self.cursor_encodings = c.strlistget("encodings.cursor")
        self.send_bell = c.boolget("bell")
        self.window_initiate_moveresize = c.boolget(
            "window.initiate-moveresize")
        self.system_tray = c.boolget("system_tray")
        self.metadata_supported = c.strlistget("metadata.supported",
                                               DEFAULT_METADATA_SUPPORTED)
        self.window_frame_sizes = typedict(
            c.dictget("window.frame_sizes") or {})
        log("cursors=%s (encodings=%s), bell=%s, notifications=%s",
            self.send_cursors, self.cursor_encodings, self.send_bell,
            self.send_notifications)
        log("client uuid %s", self.uuid)

        #window filters:
        try:
            for object_name, property_name, operator, value in c.listget(
                    "window-filters"):
                self.add_window_filter(object_name, property_name, operator,
                                       value)
        except Exception as e:
            filterslog.error("Error parsing window-filters: %s", e)
예제 #13
0
 def capsauth(self, a, challenge_response=None, client_salt=None):
     caps = typedict()
     if challenge_response is not None:
         caps["challenge_response"] = challenge_response
     if client_salt is not None:
         caps["challenge_client_salt"] = client_salt
     return a.authenticate(caps)
예제 #14
0
 def parse_client_caps(self, c):
     self.send_windows = c.boolget("ui_client", True) and c.boolget(
         "windows", True)
     self.pointer_grabs = c.boolget("pointer.grabs")
     self.send_cursors = self.send_windows and c.boolget("cursors")
     self.cursor_encodings = c.strtupleget("encodings.cursor")
     self.send_bell = c.boolget("bell")
     self.system_tray = c.boolget("system_tray")
     self.metadata_supported = c.strtupleget("metadata.supported",
                                             DEFAULT_METADATA_SUPPORTED)
     log("metadata supported=%s", self.metadata_supported)
     self.window_frame_sizes = typedict(c.dictget("window.frame_sizes", {}))
     self.window_min_size = c.inttupleget("window.min-size", (0, 0))
     self.window_max_size = c.inttupleget("window.max-size", (0, 0))
     self.window_restack = c.boolget("window.restack", False)
     log("cursors=%s (encodings=%s), bell=%s", self.send_cursors,
         self.cursor_encodings, self.send_bell)
     #window filters:
     try:
         for object_name, property_name, operator, value in c.tupleget(
                 "window-filters"):
             self.add_window_filter(object_name, property_name, operator,
                                    value)
     except Exception as e:
         filterslog.error("Error parsing window-filters: %s", e)
예제 #15
0
    def do_video_paint(self, img, x, y, enc_width, enc_height, width, height,
                       options, callbacks):
        target_rgb_formats = self.RGB_MODES
        #as some video formats like vpx can forward transparency
        #also we could skip the csc step in some cases:
        pixel_format = img.get_pixel_format()
        cd = self._csc_decoder
        if cd is not None:
            if cd.get_src_format() != pixel_format:
                log("do_video_paint csc: switching src format from %s to %s",
                    cd.get_src_format(), pixel_format)
                self.do_clean_csc_decoder()
            elif cd.get_dst_format() not in target_rgb_formats:
                log("do_video_paint csc: switching dst format from %s to %s",
                    cd.get_dst_format(), target_rgb_formats)
                self.do_clean_csc_decoder()
            elif cd.get_src_width() != enc_width or cd.get_src_height(
            ) != enc_height:
                log(
                    "do_video_paint csc: switching src size from %sx%s to %sx%s",
                    enc_width, enc_height, cd.get_src_width(),
                    cd.get_src_height())
                self.do_clean_csc_decoder()
            elif cd.get_dst_width() != width or cd.get_dst_height() != height:
                log(
                    "do_video_paint csc: switching src size from %sx%s to %sx%s",
                    width, height, cd.get_dst_width(), cd.get_dst_height())
                self.do_clean_csc_decoder()
        if self._csc_decoder is None:
            #use higher quality csc to compensate for lower quality source
            #(which generally means that we downscaled via YUV422P or lower)
            #or when upscaling the video:
            q = options.intget("quality", 50)
            csc_speed = int(
                min(100, 100 - q,
                    100.0 * (enc_width * enc_height) / (width * height)))
            cd = self.make_csc(enc_width, enc_height, pixel_format, width,
                               height, target_rgb_formats, csc_speed)
            log("do_video_paint new csc decoder: %s", cd)
            self._csc_decoder = cd
        rgb_format = cd.get_dst_format()
        rgb = cd.convert_image(img)
        log("do_video_paint rgb using %s.convert_image(%s)=%s", cd, img, rgb)
        img.free()
        assert rgb.get_planes() == 0, "invalid number of planes for %s: %s" % (
            rgb_format, rgb.get_planes())
        #make a new options dict and set the rgb format:
        paint_options = typedict(options)

        #this will also take care of firing callbacks (from the UI thread):
        def paint():
            data = rgb.get_pixels()
            rowstride = rgb.get_rowstride()
            try:
                self.do_paint_rgb(rgb_format, data, x, y, width, height,
                                  rowstride, paint_options, callbacks)
            finally:
                rgb.free()

        self.idle_add(paint)
예제 #16
0
 def _set_client_properties(self, proto, wid, window,
                            new_client_properties):
     """
     Allows us to keep window properties for a client after disconnection.
     (we keep it in a map with the client's uuid as key)
     """
     ss = self._server_sources.get(proto)
     if ss:
         ss.set_client_properties(wid, window,
                                  typedict(new_client_properties))
         #filter out encoding properties, which are expected to be set everytime:
         ncp = {}
         for k, v in new_client_properties.items():
             if v is None:
                 log.warn("removing invalid None property for %s", k)
                 continue
             if not k.startswith(b"encoding"):
                 ncp[k] = v
         if ncp:
             log(
                 "set_client_properties updating window %s of source %s with %s",
                 wid, ss.uuid, ncp)
             client_properties = self.client_properties.setdefault(
                 wid, {}).setdefault(ss.uuid, {})
             client_properties.update(ncp)
예제 #17
0
    def _process_hello(self, proto, packet):
        capabilities = packet[1]
        c = typedict(capabilities)
        proto.set_compression_level(c.intget("compression_level", self.compression_level))
        proto.enable_compressor_from_caps(c)
        if not proto.enable_encoder_from_caps(c):
            #this should never happen:
            #if we got here, we parsed a packet from the client!
            #(maybe the client used an encoding it claims not to support?)
            self.disconnect_client(proto, PROTOCOL_ERROR, "failed to negotiate a packet encoder")
            return

        log("process_hello: capabilities=%s", capabilities)
        if c.boolget("version_request"):
            self.send_version_info(proto)
            return

        auth_caps = self.verify_hello(proto, c)
        if auth_caps is not False:
            if c.boolget("info_request", False):
                self.send_hello_info(proto)
                return
            command_req = c.strlistget("command_request")
            if len(command_req)>0:
                #call from UI thread:
                self.idle_add(self.handle_command_request, proto, command_req)
                return
            #continue processing hello packet in UI thread:
            self.idle_add(self.call_hello_oked, proto, packet, c, auth_caps)
예제 #18
0
 def init_window(self, metadata):
     self._backing = None
     self._metadata = typedict()
     # used for only sending focus events *after* the window is mapped:
     self._been_mapped = False
     self._override_redirect_windows = []
     self.update_metadata(metadata)
 def split_vscroll(self, i=1):
     W, H = self.window.get_size()
     scrolls = [
                (0,      H//2,   W,      H//2-i,     0,  i),
                (0,      i,      W,      H//2-i,     0,  -i),
                ]
     self.window.draw_region(0, 0, W, H, "scroll", scrolls, W*4, 0, typedict({}), [])
예제 #20
0
    def __init__(self, client, group_leader, wid, x, y, ww, wh, bw, bh, metadata, override_redirect, client_properties, border, max_window_size):
        log("%s%s", type(self), (client, group_leader, wid, x, y, ww, wh, bw, bh, metadata, override_redirect, client_properties, max_window_size))
        ClientWidgetBase.__init__(self, client, wid, metadata.boolget("has-alpha"))
        self._override_redirect = override_redirect
        self.group_leader = group_leader
        self._pos = (x, y)
        self._size = (ww, wh)
        self._client_properties = client_properties
        self._set_initial_position = False
        self.size_constraints = typedict()
        self.geometry_hints = {}
        self._fullscreen = None
        self._maximized = False
        self._above = False
        self._below = False
        self._shaded = False
        self._sticky = False
        self._skip_pager = False
        self._skip_taskbar = False
        self._sticky = False
        self._iconified = False
        self._focused = False
        self.border = border
        self.max_window_size = max_window_size
        self.button_state = {}

        self.init_window(metadata)
        self.setup_window(bw, bh)
        self.update_metadata(metadata)
예제 #21
0
 def process_server_packet(self, proto, packet):
     packet_type = packet[0]
     log("process_server_packet: %s", packet_type)
     if packet_type==Protocol.CONNECTION_LOST:
         self.stop("server connection lost", proto)
         return
     elif packet_type=="disconnect":
         log("got disconnect from server: %s", packet[1])
         if self.exit:
             self.server_protocol.close()
         else:
             self.stop("disconnect from server: %s" % packet[1])
     elif packet_type=="hello":
         c = typedict(packet[1])
         maxw, maxh = c.intpair("max_desktop_size", (4096, 4096))
         caps = self.filter_server_caps(c)
         #add new encryption caps:
         if self.cipher:
             from xpra.net.crypto import crypto_backend_init, new_cipher_caps, DEFAULT_PADDING
             crypto_backend_init()
             padding_options = self.caps.strlistget("cipher.padding.options", [DEFAULT_PADDING])
             auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key, padding_options)
             caps.update(auth_caps)
         #may need to bump packet size:
         proto.max_packet_size = maxw*maxh*4*4
         file_transfer = self.caps.boolget("file-transfer") and c.boolget("file-transfer")
         file_size_limit = max(self.caps.intget("file-size-limit"), c.intget("file-size-limit"))
         file_max_packet_size = int(file_transfer) * (1024 + file_size_limit*1024*1024)
         self.client_protocol.max_packet_size = max(self.client_protocol.max_packet_size, file_max_packet_size)
         self.server_protocol.max_packet_size = max(self.server_protocol.max_packet_size, file_max_packet_size)
         packet = ("hello", caps)
     elif packet_type=="info-response":
         #adds proxy info:
         #note: this is only seen by the client application
         #"xpra info" is a new connection, which talks to the proxy server...
         info = packet[1]
         info.update(self.get_proxy_info(proto))
     elif packet_type=="lost-window":
         wid = packet[1]
         #mark it as lost so we can drop any current/pending frames
         self.lost_windows.add(wid)
         #queue it so it gets cleaned safely (for video encoders mostly):
         self.encode_queue.put(packet)
         #and fall through so tell the client immediately
     elif packet_type=="draw":
         #use encoder thread:
         self.encode_queue.put(packet)
         #which will queue the packet itself when done:
         return
     #we do want to reformat cursor packets...
     #as they will have been uncompressed by the network layer already:
     elif packet_type=="cursor":
         #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name]
         #or:
         #packet = ["cursor", ""]
         self._packet_recompress(packet, 8, "cursor")
     elif packet_type=="window-icon":
         self._packet_recompress(packet, 5, "icon")
     self.queue_client_packet(packet)
예제 #22
0
    def do_video_paint(self, img, x, y, enc_width, enc_height, width, height, options, callbacks):
        target_rgb_formats = self.RGB_MODES
        # as some video formats like vpx can forward transparency
        # also we could skip the csc step in some cases:
        pixel_format = img.get_pixel_format()
        cd = self._csc_decoder
        if cd is not None:
            if cd.get_src_format() != pixel_format:
                log("do_video_paint csc: switching src format from %s to %s", cd.get_src_format(), pixel_format)
                self.do_clean_csc_decoder()
            elif cd.get_dst_format() not in target_rgb_formats:
                log("do_video_paint csc: switching dst format from %s to %s", cd.get_dst_format(), target_rgb_formats)
                self.do_clean_csc_decoder()
            elif cd.get_src_width() != enc_width or cd.get_src_height() != enc_height:
                log(
                    "do_video_paint csc: switching src size from %sx%s to %sx%s",
                    enc_width,
                    enc_height,
                    cd.get_src_width(),
                    cd.get_src_height(),
                )
                self.do_clean_csc_decoder()
            elif cd.get_dst_width() != width or cd.get_dst_height() != height:
                log(
                    "do_video_paint csc: switching src size from %sx%s to %sx%s",
                    width,
                    height,
                    cd.get_dst_width(),
                    cd.get_dst_height(),
                )
                self.do_clean_csc_decoder()
        if self._csc_decoder is None:
            # use higher quality csc to compensate for lower quality source
            # (which generally means that we downscaled via YUV422P or lower)
            # or when upscaling the video:
            q = options.intget("quality", 50)
            csc_speed = int(min(100, 100 - q, 100.0 * (enc_width * enc_height) / (width * height)))
            cd = self.make_csc(enc_width, enc_height, pixel_format, width, height, target_rgb_formats, csc_speed)
            log("do_video_paint new csc decoder: %s", cd)
            self._csc_decoder = cd
        rgb_format = cd.get_dst_format()
        rgb = cd.convert_image(img)
        log("do_video_paint rgb using %s.convert_image(%s)=%s", cd, img, rgb)
        img.free()
        assert rgb.get_planes() == 0, "invalid number of planes for %s: %s" % (rgb_format, rgb.get_planes())
        # make a new options dict and set the rgb format:
        paint_options = typedict(options)
        paint_options["rgb_format"] = rgb_format
        # this will also take care of firing callbacks (from the UI thread):
        def paint():
            data = rgb.get_pixels()
            rowstride = rgb.get_rowstride()
            try:
                self.do_paint_rgb(rgb_format, data, x, y, width, height, rowstride, paint_options, callbacks)
            finally:
                rgb.free()

        self.idle_add(paint)
예제 #23
0
 def __init__(self, window_class, client, wid, W=630, H=480, animate=True):
     self.wid = wid
     self.window = window_class(client, None, wid, 10, 10, W, H, W, H, typedict({}), False, typedict({}), 0, None)
     self.window.show()
     self.paint_rect(0, 0, W, H, chr(255)*4*W*H)
     self.paint_rect(W//2-16, H//2-16, 32, 32, chr(0)*4*32*32)
     self.animate = animate
     self.damage = False
     client.handle_key_action = self.handle_key_action
예제 #24
0
 def __init__(self, window_class, client, wid=1, W=630, H=480):
     self.wid = wid
     self.window = window_class(client, None, wid, 10, 10, W, H, W, H, typedict({}), False, typedict({}), 0, None)
     self.window.show()
     self.paint_rect(0, 0, W, H, chr(255)*4*W*H)
     self.paint_rect(W//2-16, H//2-16, 64, 64, chr(0)*4*64*64)
     self.counter = 0
     self.delta_x = 0
     self.delta_y = 0
예제 #25
0
 def _process_challenge(self, packet):
     authlog("processing challenge: %s", packet[1:])
     def warn_server_and_exit(code, message, server_message="authentication failed"):
         authlog.error("Error: authentication failed:")
         authlog.error(" %s", message)
         self.disconnect_and_quit(code, server_message)
     if not self.password_file and not os.environ.get('XPRA_PASSWORD'):
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "this server requires authentication, please provide a password", "no password available")
         return
     password = self.load_password()
     if not password:
         warn_server_and_exit(EXIT_PASSWORD_FILE_ERROR, "failed to load password from file %s" % self.password_file, "no password available")
         return
     salt = packet[1]
     if self.encryption:
         assert len(packet)>=3, "challenge does not contain encryption details to use for the response"
         server_cipher = typedict(packet[2])
         key = self.get_encryption_key()
         if key is None:
             warn_server_and_exit(EXIT_ENCRYPTION, "the server does not use any encryption", "client requires encryption")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     #all server versions support a client salt,
     #they also tell us which digest to use:
     digest = packet[3]
     client_salt = get_hex_uuid()+get_hex_uuid()
     #TODO: use some key stretching algorigthm? (meh)
     try:
         from xpra.codecs.xor.cyxor import xor_str           #@UnresolvedImport
         salt = xor_str(salt, client_salt)
     except:
         salt = xor(salt, client_salt)
     if digest==b"hmac":
         import hmac, hashlib
         def s(v):
             try:
                 return v.encode()
             except:
                 return str(v)
         password = s(password)
         salt = s(salt)
         challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest()
     elif digest==b"xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             warn_server_and_exit(EXIT_ENCRYPTION, "server requested digest %s, cowardly refusing to use it without encryption" % digest, "invalid digest")
             return
         challenge_response = xor(password, salt)
     else:
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "server requested an unsupported digest: %s" % digest, "invalid digest")
         return
     if digest:
         authlog("%s(%s, %s)=%s", digest, binascii.hexlify(password), binascii.hexlify(salt), challenge_response)
     self.password_sent = True
     self.remove_packet_handlers("challenge")
     self.send_hello(challenge_response, client_salt)
예제 #26
0
    def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
        """ can be called from any thread """
        # log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
        PIL = get_codec("PIL")
        assert PIL.Image, "PIL.Image not found"
        buf = BytesIOClass(img_data)
        img = PIL.Image.open(buf)
        assert img.mode in ("L", "P", "RGB", "RGBA"), "invalid image mode: %s" % img.mode
        transparency = options.get("transparency", -1)
        if img.mode == "P":
            if transparency >= 0:
                # this deals with alpha without any extra work
                img = img.convert("RGBA")
            else:
                img = img.convert("RGB")
        elif img.mode == "L":
            if transparency >= 0:
                # why do we have to deal with alpha ourselves??
                def mask_value(a):
                    if a != transparency:
                        return 255
                    return 0

                mask = PIL.Image.eval(img, mask_value)
                mask = mask.convert("L")

                def nomask_value(a):
                    if a != transparency:
                        return a
                    return 0

                img = PIL.Image.eval(img, nomask_value)
                img = img.convert("RGBA")
                img.putalpha(mask)
            else:
                img = img.convert("RGB")

        # use tobytes() if present, fallback to tostring():
        data_fn = getattr(img, "tobytes", getattr(img, "tostring", None))
        raw_data = data_fn("raw", img.mode)
        paint_options = typedict(options)
        rgb_format = img.mode
        if rgb_format == "RGB":
            # PIL flattens the data to a continuous straightforward RGB format:
            rowstride = width * 3
            img_data = self.process_delta(raw_data, width, height, rowstride, options)
        elif rgb_format == "RGBA":
            rowstride = width * 4
            img_data = self.process_delta(raw_data, width, height, rowstride, options)
        else:
            raise Exception("invalid image mode: %s" % img.mode)
        paint_options["rgb_format"] = rgb_format
        self.idle_add(self.do_paint_rgb, rgb_format, img_data, x, y, width, height, rowstride, paint_options, callbacks)
        return False
예제 #27
0
 def _process_hello(self, packet):
     if not self.password_sent and self.password_file:
         self.warn_and_quit(EXIT_NO_AUTHENTICATION, "the server did not request our password")
         return
     try:
         self.server_capabilities = packet[1]
         log("processing hello from server: %s", self.server_capabilities)
         c = typedict(self.server_capabilities)
         self.parse_server_capabilities(c)
     except Exception, e:
         self.warn_and_quit(EXIT_FAILURE, "error processing hello packet from server: %s" % e)
예제 #28
0
 def _process_hello(self, packet):
     if not self.password_sent and (self.password_file or os.environ.get('XPRA_PASSWORD')):
         self.warn_and_quit(EXIT_NO_AUTHENTICATION, "the server did not request our password")
         return
     try:
         self.server_capabilities = typedict(packet[1])
         log("processing hello from server: %s", self.server_capabilities)
         self.server_connection_established()
     except Exception as e:
         log.info("error in hello packet", exc_info=True)
         self.warn_and_quit(EXIT_FAILURE, "error processing hello packet from server: %s" % e)
예제 #29
0
 def _process_send_file(self, packet):
     #the remote end is sending us a file
     basefilename, mimetype, printit, openit, filesize, file_data, options = packet[1:8]
     options = typedict(options)
     if printit:
         l = printlog
         assert self.printing
     else:
         l = filelog
         assert self.file_transfer
     l("receiving file: %s", [basefilename, mimetype, printit, openit, filesize, "%s bytes" % len(file_data), options])
     assert filesize>0, "invalid file size: %s" % filesize
     if filesize>self.file_size_limit*1024*1024:
         l.error("Error: file '%s' is too large:", basefilename)
         l.error(" %iMB, the file size limit is %iMB", filesize//1024//1024, self.file_size_limit)
         return
     filename, fd = safe_open_download_file(basefilename, mimetype)
     self.file_descriptors.add(fd)
     chunk_id = options.get("file-chunk-id")
     if chunk_id:
         if len(self.receive_chunks_in_progress)>=MAX_CONCURRENT_FILES:
             self.send("ack-file-chunk", chunk_id, False, "too many file transfers in progress", 0)
             os.close(fd)
             return
         digest = hashlib.sha1()
         chunk = 0
         timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_receiving, chunk_id, chunk)
         chunk_state = [time.time(), fd, filename, mimetype, printit, openit, filesize, options, digest, 0, timer, chunk]
         self.receive_chunks_in_progress[chunk_id] = chunk_state
         self.send("ack-file-chunk", chunk_id, True, "", chunk)
         return
     #not chunked, full file:
     assert file_data, "no data!"
     if len(file_data)!=filesize:
         l.error("Error: invalid data size for file '%s'", basefilename)
         l.error(" received %i bytes, expected %i bytes", len(file_data), filesize)
         return
     #check digest if present:
     def check_digest(algo="sha1", libfn=hashlib.sha1):
         digest = options.get(algo)
         if digest:
             u = libfn()
             u.update(file_data)
             l("%s digest: %s - expected: %s", algo, u.hexdigest(), digest)
             self.check_digest(basefilename, u.hexdigest(), digest, algo)
     check_digest("sha1", hashlib.sha1)
     check_digest("md5", hashlib.md5)
     try:
         os.write(fd, file_data)
     finally:
         os.close(fd)
     self.do_process_downloaded_file(filename, mimetype, printit, openit, filesize, options)
예제 #30
0
파일: wrapper.py 프로젝트: svn2github/Xpra
def query_sound():
    import subprocess
    command = get_sound_command()+["_sound_query"]
    _add_debug_args(command)
    kwargs = exec_kwargs()
    env = exec_env()
    env.update(get_sound_wrapper_env())
    log("query_sound() command=%s, env=%s, kwargs=%s", command, env, kwargs)
    proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, env=env, **kwargs)
    out, err = proc.communicate(None)
    log("query_sound() process returned %s", proc.returncode)
    log("query_sound() out=%s, err=%s", out, err)
    if proc.returncode!=0:
        return typedict()
    d = typedict()
    for x in out.decode("utf8").splitlines():
        kv = x.split("=", 1)
        if len(kv)==2:
            #ie: kv = ["decoders", "mp3,vorbis"]
            d[kv[0].encode()] = [x.encode() for x in kv[1].split(",")]     #d["decoders"] = ["mp3", "vorbis"]
    log("query_sound()=%s", d)
    return d
예제 #31
0
 def movearound(self, ydelta=1):
     self.counter += 1
     RADIUS = 128
     target_x = int(math.sin(self.counter/10.0) * RADIUS)
     target_y = int(math.cos(self.counter/10.0) * RADIUS)
     dx = target_x - self.delta_x
     dy = target_y - self.delta_y
     W, H = self.window.get_size()
     scrolls = (RADIUS, RADIUS, W-RADIUS*2, H-RADIUS*2, dx, dy),
     self.window.draw_region(0, 0, W, H, "scroll", scrolls, W*4, 0, typedict({"flush" : 0}), [])
     self.delta_x = target_x
     self.delta_y = target_y
     return True
예제 #32
0
파일: server.py 프로젝트: svn2github/Xpra
 def _get_antialias_hintstyle(self):
     ad = typedict(self.antialias)
     hintstyle = ad.strget("hintstyle", "").lower()
     if hintstyle in ("hintnone", "hintslight", "hintmedium", "hintfull"):
         #X11 clients can give us what we need directly:
         return hintstyle
     #win32 style contrast value:
     contrast = ad.intget("contrast", -1)
     if contrast>1600:
         return "hintfull"
     elif contrast>1000:
         return "hintmedium"
     elif contrast>0:
         return "hintslight"
     return "hintnone"
예제 #33
0
    def process_server_packet(self, proto, packet):
        packet_type = packet[0]
        debug("process_server_packet: %s", packet_type)
        if packet_type==Protocol.CONNECTION_LOST:
            self.stop("server connection lost", proto)
            return
        elif packet_type=="hello":
            c = typedict(packet[1])
            maxw, maxh = c.intpair("max_desktop_size", (4096, 4096))
            proto.max_packet_size = maxw*maxh*4

            caps = self.filter_server_caps(c)
            #add new encryption caps:
            if self.cipher:
                auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key)
                caps.update(auth_caps)
            packet = ("hello", caps)
        elif packet_type=="info-response":
            #adds proxy info:
            #note: this is only seen by the client application
            #"xpra info" is a new connection, which talks to the proxy server...
            info = packet[1]
            info.update(get_server_info("proxy."))
            info.update(get_thread_info("proxy.", proto))
            info.update(self.get_encoder_info())
        elif packet_type=="lost-window":
            wid = packet[1]
            #mark it as lost so we can drop any current/pending frames
            self.lost_windows.add(wid)
            #queue it so it gets cleaned safely (for video encoders mostly):
            self.encode_queue.put(packet)
            #and fall through so tell the client immediately
        elif packet_type=="draw":
            #use encoder thread:
            self.encode_queue.put(packet)
            #which will queue the packet itself when done:
            return
        elif packet_type=="cursor":
            #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name]
            #or:
            #packet = ["cursor", ""]
            if len(packet)>=9:
                pixels = packet[8]
                if len(pixels)<64:
                    packet[8] = str(pixels)
                else:
                    packet[8] = compressed_wrapper("cursor", pixels)
        self.queue_client_packet(packet)
예제 #34
0
    def process_server_packet(self, proto, packet):
        packet_type = packet[0]
        debug("process_server_packet: %s", packet_type)
        if packet_type==Protocol.CONNECTION_LOST:
            self.stop("server connection lost", proto)
            return
        elif packet_type=="hello":
            c = typedict(packet[1])
            maxw, maxh = c.intpair("max_desktop_size", (4096, 4096))
            proto.max_packet_size = maxw*maxh*4

            caps = self.filter_server_caps(c)
            #add new encryption caps:
            if self.cipher:
                auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key)
                caps.update(auth_caps)
            packet = ("hello", caps)
        elif packet_type=="info-response":
            #adds proxy info:
            #note: this is only seen by the client application
            #"xpra info" is a new connection, which talks to the proxy server...
            info = packet[1]
            info.update(get_server_info("proxy."))
            info.update(get_thread_info("proxy.", proto))
            info.update(self.get_encoder_info())
        elif packet_type=="lost-window":
            wid = packet[1]
            ve = self.video_encoders.get(wid)
            if ve:
                ve.clean()
                del self.video_encoders[wid]
        elif packet_type=="draw":
            self.process_draw(packet)
        elif packet_type=="cursor":
            #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name]
            #or:
            #packet = ["cursor", ""]
            if len(packet)>=9:
                pixels = packet[8]
                if len(pixels)<64:
                    packet[8] = str(pixels)
                else:
                    packet[8] = compressed_wrapper("cursor", pixels)
        self.queue_client_packet(packet)
예제 #35
0
 def init_window(self, metadata):
     self._backing = None
     self._metadata = typedict()
     # used for only sending focus events *after* the window is mapped:
     self._been_mapped = False
     self._override_redirect_windows = []
     if "workspace" in self._client_properties:
         workspace = self._client_properties.get("workspace")
         if workspace is not None:
             workspacelog("workspace from client properties: %s", workspace)
             #client properties override application specified workspace value on init only:
             metadata["workspace"] = int(workspace)
     self._window_workspace = WORKSPACE_UNSET        #will get set in set_metadata if present
     self._desktop_workspace = self.get_desktop_workspace()
     workspacelog("init_window(..) workspace=%s, current workspace=%s", self._window_workspace, self._desktop_workspace)
     if self.max_window_size and b"size-constraints" not in metadata:
         #this ensures that we will set size-constraints and honour max_window_size:
         metadata[b"size-constraints"] = {}
     self.update_metadata(metadata)
예제 #36
0
파일: server_core.py 프로젝트: ljmljz/xpra
    def _process_hello(self, proto, packet):
        capabilities = packet[1]
        c = typedict(capabilities)
        proto.set_compression_level(c.intget("compression_level", self.compression_level))
        proto.enable_compressor_from_caps(c)
        if not proto.enable_encoder_from_caps(c):
            #this should never happen:
            #if we got here, we parsed a packet from the client!
            #(maybe the client used an encoding it claims not to support?)
            self.disconnect_client(proto, PROTOCOL_ERROR, "failed to negotiate a packet encoder")
            return

        log("process_hello: capabilities=%s", capabilities)
        if c.boolget("version_request"):
            self.send_version_info(proto)
            return

        auth_caps = self.verify_hello(proto, c)
        if auth_caps is not False:
            if c.boolget("info_request", False):
                flatten = not c.boolget("info-namespace", False)
                self.send_hello_info(proto, flatten)
                return
            command_req = c.strlistget("command_request")
            if len(command_req)>0:
                #call from UI thread:
                self.idle_add(self.handle_command_request, proto, *command_req)
                return
            #continue processing hello packet:
            try:
                if SIMULATE_SERVER_HELLO_ERROR:
                    raise Exception("Simulating a server error")
                self.hello_oked(proto, packet, c, auth_caps)
            except ClientException as e:
                log.error("Error setting up new connection for")
                log.error(" %s:", proto)
                log.error(" %s", e)
                self.disconnect_client(proto, SERVER_ERROR, str(e))
            except Exception as e:
                #log exception but don't disclose internal details to the client
                log.error("server error processing new connection from %s: %s", proto, e, exc_info=True)
                self.disconnect_client(proto, SERVER_ERROR, "error accepting new connection")
예제 #37
0
    def process_server_packet(self, proto, packet):
        packet_type = packet[0]
        debug("process_server_packet: %s", packet_type)
        if packet_type==Protocol.CONNECTION_LOST:
            self.stop("server connection lost", proto)
            return
        elif packet_type=="hello":
            c = typedict(packet[1])
            maxw, maxh = c.intpair("max_desktop_size", (4096, 4096))
            proto.max_packet_size = maxw*maxh*4

            caps = self.filter_server_caps(c)
            #add new encryption caps:
            if self.cipher:
                auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key)
                caps.update(auth_caps)
            packet = ("hello", caps)
        elif packet_type=="info-response":
            #adds proxy info:
            info = packet[1]
            info.update(get_server_info("proxy."))
            info.update(get_thread_info("proxy.", proto))
        elif packet_type=="draw":
            #packet = ["draw", wid, x, y, outw, outh, coding, data, self._damage_packet_sequence, outstride, client_options]
            #ensure we don't try to re-compress the pixel data in the network layer:
            #(re-add the "compressed" marker that gets lost when we re-assemble packets)
            coding = packet[6]
            if coding!="mmap":
                data = packet[7]
                packet[7] = Compressed("%s pixels" % coding, data)
        elif packet_type=="cursor":
            #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name]
            #or:
            #packet = ["cursor", ""]
            if len(packet)>=9:
                pixels = packet[8]
                if len(pixels)<64:
                    packet[8] = str(pixels)
                else:
                    packet[8] = compressed_wrapper("cursor", pixels)
        self.queue_client_packet(packet)
예제 #38
0
 def paint_webp_using_webm(self, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     dec_webm = get_codec("dec_webm")
     assert dec_webm is not None, "webp decoder not found"
     paint_options = typedict(options)
     if options.get("has_alpha", False):
         decode = dec_webm.DecodeRGBA
         rowstride = width*4
         paint_rgb = self.do_paint_rgb32
         paint_options["rgb_format"] = "RGBA"
     else:
         decode = dec_webm.DecodeRGB
         rowstride = width*3
         paint_rgb = self.do_paint_rgb24
         paint_options["rgb_format"] = "RGB"
     log("paint_webp(%s) using decode=%s, paint=%s, paint_options=%s",
          ("%s bytes" % len(img_data), x, y, width, height, options, callbacks), decode, paint_rgb, paint_options)
     rgb_data = decode(img_data)
     pixels = str(rgb_data.bitmap)
     self.idle_add(paint_rgb, pixels, x, y, width, height, rowstride, paint_options, callbacks)
     return  False
예제 #39
0
 def set_strut(self, strut):
     if not HAS_X11_BINDINGS:
         return
     log("strut=%s", strut)
     d = typedict(strut)
     values = []
     for x in ("left", "right", "top", "bottom"):
         values.append(d.intget(x, 0))
     has_partial = False
     for x in ("left_start_y", "left_end_y",
               "right_start_y", "right_end_y",
               "top_start_x", "top_end_x",
               "bottom_start_x", "bottom_end_x"):
         if x in d:
             has_partial = True
         values.append(d.intget(x, 0))
     log("setting strut=%s, has partial=%s", values, has_partial)
     if not self.is_realized():
         self.realize()
     if has_partial:
         prop_set(self.get_window(), "_NET_WM_STRUT_PARTIAL", ["u32"], values)
     prop_set(self.get_window(), "_NET_WM_STRUT", ["u32"], values[:4])
예제 #40
0
 def scrollup(self, ydelta=1):
     if not self.animate:
         return True
     print("scrollup(%s) damage=%s" % (ydelta, self.damage))
     W, H = self.window.get_size()
     scrolls = (0, ydelta, W, H-ydelta, 0, -ydelta),
     self.window.draw_region(0, 0, W, H, "scroll", scrolls, W*4, 0, typedict({"flush" : 0}), [])
     dots = []
     v = int(time.time()*10000)
     for _ in range(W*ydelta):
         CB = 0xFF << ((self.wid % 4) * 8)
         c = struct.pack("@I", v & 0xFFFFFFFF & ~CB)
         dots.append(c)
     img_data = b"".join(dots)
     self.paint_rect(0, H-ydelta, W, ydelta, img_data)
     if self.damage:
         c = struct.pack("@I", 0xFFFFFFFF)
         img_data = c*10*16
         self.paint_rect(W-10, H//2-8-16, 10, 16, img_data)
         c = struct.pack("@I", 0x000000FF)
         img_data = c*10*16
         self.paint_rect(W-10, H//2-8, 10, 16, img_data)
     return True
예제 #41
0
    def _process_send_file(self, packet):
        #send-file basefilename, printit, openit, filesize, 0, data)
        from xpra.platform.features import DOWNLOAD_PATH
        basefilename, mimetype, printit, openit, filesize, file_data, options = packet[1:11]
        filelog("received file: %s", [basefilename, mimetype, printit, openit, filesize, "%s bytes" % len(file_data), options])
        options = typedict(options)
        if printit:
            assert self.printing
        else:
            assert self.file_transfer
        assert filesize>0 and file_data
        if len(file_data)!=filesize:
            log.error("Error: invalid data size for file '%s'", basefilename)
            log.error(" received %i bytes, expected %i bytes", len(file_data), filesize)
            return
        if filesize>self.file_size_limit*1024*1024:
            log.error("Error: file '%s' is too large:", basefilename)
            log.error(" %iMB, the file size limit is %iMB", filesize//1024//1024, self.file_size_limit)
            return
        #check digest if present:
        digest = options.get("sha1")
        if digest:
            import hashlib
            u = hashlib.sha1()
            u.update(file_data)
            filelog("sha1 digest: %s - expected: %s", u.hexdigest(), digest)
            assert digest==u.hexdigest(), "invalid file digest %s (expected %s)" % (u.hexdigest(), digest)

        #make sure we use a filename that does not exist already:
        wanted_filename = os.path.abspath(os.path.join(os.path.expanduser(DOWNLOAD_PATH), os.path.basename(basefilename)))
        EXTS = {"application/postscript"    : "ps",
                "application/pdf"           : "pdf",
                }
        ext = EXTS.get(mimetype)
        if ext:
            #on some platforms (win32),
            #we want to force an extension
            #so that the file manager can display them properly when you double-click on them
            if not wanted_filename.endswith("."+ext):
                wanted_filename += "."+ext
        filename = wanted_filename
        base = 0
        while os.path.exists(filename):
            filelog("cannot save file as %s: file already exists", filename)
            root, ext = os.path.splitext(wanted_filename)
            base += 1
            filename = root+("-%s" % base)+ext
        flags = os.O_CREAT | os.O_RDWR | os.O_EXCL
        try:
            flags |= os.O_BINARY                #@UndefinedVariable (win32 only)
        except:
            pass
        fd = os.open(filename, flags)
        try:
            os.write(fd, file_data)
        finally:
            os.close(fd)
        filelog.info("downloaded %s bytes to %s file %s", filesize, mimetype, filename)
        if printit:
            printer = options.strget("printer")
            title   = options.strget("title")
            print_options = options.dictget("options")
            #TODO: how do we print multiple copies?
            #copies = options.intget("copies")
            #whitelist of options we can forward:
            safe_print_options = dict((k,v) for k,v in print_options.items() if k in ("PageSize", "Resolution"))
            printlog("safe print options(%s) = %s", options, safe_print_options)
            self._print_file(filename, printer, title, safe_print_options)
            return
        elif openit:
            #run the command in a new thread
            #so we can block waiting for the subprocess to exit
            #(ensures that we do reap the process)
            import thread
            thread.start_new_thread(self._open_file, (filename, ))