def get_icc_info(): try: data = _get_X11_root_property("_ICC_PROFILE", "CARDINAL") if data: screenlog("_ICC_PROFILE=%s (%s)", type(data), len(data)) version = _get_X11_root_property("_ICC_PROFILE_IN_X_VERSION", "CARDINAL") screenlog( "get_icc_info() found _ICC_PROFILE_IN_X_VERSION=%s, _ICC_PROFILE=%s", hexstr(version or ""), hexstr(data)) icc = { "source": "_ICC_PROFILE", "data": data, } if version: try: version = ord(version) except: pass icc["version"] = version return icc except Exception as e: screenlog.error( "Error: cannot access _ICC_PROFILE X11 window property") screenlog.error(" %s", e) screenlog("get_icc_info()", exc_info=True) return {}
def _parse_security_handshake(self, packet): log("parse_security_handshake(%s)", hexstr(packet)) n = struct.unpack(b"B", packet[:1])[0] if n == 0: self._internal_error("cannot parse security handshake '%s'" % hexstr(packet)) return 0 security_types = struct.unpack(b"B" * n, packet[1:]) st = [] for v in security_types: try: v = RFBAuth(v) except ValueError: pass st.append(v) log("parse_security_handshake(%s) security_types=%s", hexstr(packet), [AUTH_STR.get(v, v) for v in st]) if not st or RFBAuth.NONE in st: auth_type = RFBAuth.NONE #go straight to the result: self._packet_parser = self._parse_security_result elif RFBAuth.VNC in st: auth_type = RFBAuth.VNC self._packet_parser = self._parse_vnc_security_challenge else: self._internal_error("no supported security types in %r" % csv(AUTH_STR.get(v, v) for v in st)) return 0 self.send_struct(b"B", auth_type) return 1 + n
def _parse_challenge(self, response): authlog("parse_challenge(%s)", hexstr(response)) if len(response) < 16: return 0 assert self._authenticator try: assert len(response) == 16 hex_response = hexstr(response) #log("padded password=%s", password) caps = typedict({ "challenge_response": hex_response, }) if self._authenticator.authenticate(caps): authlog("challenge authentication succeeded") self.send(struct.pack(b"!I", 0)) self._packet_parser = self._parse_security_result return 16 authlog.warn("Warning: authentication challenge response failure") authlog.warn(" password does not match") except Exception as e: authlog("parse_challenge(%s)", hexstr(response), exc_info=True) authlog.error("Error: authentication challenge failure:") authlog.error(" %s", e) self.timeout_add(1000, self.send_fail_challenge) return len(response)
def get_icc_info(): if not is_Wayland(): try: data = _get_X11_root_property("_ICC_PROFILE", "CARDINAL") if data: screenlog("_ICC_PROFILE=%s (%s)", type(data), len(data)) version = _get_X11_root_property("_ICC_PROFILE_IN_X_VERSION", "CARDINAL") screenlog( "get_icc_info() found _ICC_PROFILE_IN_X_VERSION=%s, _ICC_PROFILE=%s", hexstr(version or ""), hexstr(data)) icc = { "source": "_ICC_PROFILE", "data": data, } if version: try: version = ord(version) except TypeError: pass icc["version"] = version screenlog("get_icc_info()=%s", icc) return icc except Exception as e: screenlog.error( "Error: cannot access _ICC_PROFILE X11 window property") screenlog.error(" %s", e) screenlog("get_icc_info()", exc_info=True) from xpra.platform.gui import default_get_icc_info return default_get_icc_info()
def _parse_security_handshake(self, packet): log("parse_security_handshake(%s)", hexstr(packet)) try: auth = struct.unpack(b"B", packet)[0] except: self._internal_error( packet, "cannot parse security handshake response '%s'" % hexstr(packet)) return 0 auth_str = RFBAuth.AUTH_STR.get(auth, auth) if auth == RFBAuth.VNC: #send challenge: self._packet_parser = self._parse_challenge assert self._authenticator challenge, digest = self._authenticator.get_challenge("des") assert digest == "des" self._challenge = challenge[:16] log("sending RFB challenge value: %s", hexstr(self._challenge)) self.send(self._challenge) return 1 if self._authenticator and self._authenticator.requires_challenge(): self._invalid_header( packet, "invalid security handshake response, authentication is required" ) return 0 log("parse_security_handshake: auth=%s, sending SecurityResult", auth_str) #Security Handshake, send SecurityResult Handshake self._packet_parser = self._parse_security_result self.send(struct.pack(b"!I", 0)) return 1
def send_challenge_reply(self, packet, password): if not password: if self.password_file: self.auth_error( EXIT_PASSWORD_FILE_ERROR, "failed to load password from file%s %s" % (engs(self.password_file), csv(self.password_file)), "no password available") else: self.auth_error( EXIT_PASSWORD_REQUIRED, "this server requires authentication and no password is available" ) return 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: self.auth_error(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: server_salt = bytestostr(packet[1]) digest = bytestostr(packet[3]) actual_digest = digest.split(":", 1)[0] l = len(server_salt) salt_digest = "xor" if len(packet) >= 5: salt_digest = bytestostr(packet[4]) if salt_digest == "xor": #with xor, we have to match the size assert l >= 16, "server salt is too short: only %i bytes, minimum is 16" % l assert l <= 256, "server salt is too long: %i bytes, maximum is 256" % l else: #other digest, 32 random bytes is enough: l = 32 client_salt = get_salt(l) salt = gendigest(salt_digest, client_salt, server_salt) authlog("combined %s salt(%s, %s)=%s", salt_digest, hexstr(server_salt), hexstr(client_salt), hexstr(salt)) challenge_response = gendigest(actual_digest, password, salt) if not challenge_response: log("invalid digest module '%s': %s", actual_digest) self.auth_error( EXIT_UNSUPPORTED, "server requested '%s' digest but it is not supported" % actual_digest, "invalid digest") return authlog("%s(%s, %s)=%s", actual_digest, repr(password), repr(salt), repr(challenge_response)) self.do_send_challenge_reply(challenge_response, client_salt)
def _test_YUV420P(self, encoding, encoder_module, decoder_module, yuvdata, width=16, height=16): in_csc = "YUV420P" if in_csc not in encoder_module.get_input_colorspaces(encoding): raise Exception("%s does not support %s as input" % (encoder_module, in_csc)) if in_csc != decoder_module.get_output_colorspace(encoding, in_csc): raise Exception("%s does not support %s as output for %s" % (decoder_module, in_csc, in_csc)) encoder = encoder_module.Encoder() options = typedict({"max-delayed": 0}) encoder.init_context(encoding, width, height, in_csc, options) in_image = make_test_image(in_csc, width, height) yuv = [] rowstrides = [] divs = get_subsampling_divs(in_csc) for i, bvalue in enumerate(yuvdata): xdiv, ydiv = divs[i] rowstride = width // xdiv rowstrides.append(rowstride) size = rowstride * height // ydiv yuv.append(chr(bvalue).encode("latin1") * size) in_image.set_pixels(yuv) in_image.set_rowstride(rowstrides) cdata, client_options = encoder.compress_image(in_image) assert cdata #decode it: decoder = decoder_module.Decoder() decoder.init_context(encoding, width, height, in_csc) out_image = decoder.decompress_image(cdata, typedict(client_options)) #print("%s %s : %s" % (encoding, decoder_module, out_image)) in_planes = in_image.get_pixels() out_planes = out_image.get_pixels() for i, plane in enumerate(("Y", "U", "V")): in_pdata = in_planes[i] out_pdata = out_planes[i] xdiv, ydiv = divs[i] in_stride = in_image.get_rowstride()[i] out_stride = out_image.get_rowstride()[i] #compare lines at a time since the rowstride may be different: for y in range(height // ydiv): in_rowdata = in_pdata[in_stride * y:in_stride * y + width // xdiv] out_rowdata = out_pdata[out_stride * y:out_stride * y + width // xdiv] if not cmpp(in_rowdata, out_rowdata): raise Exception( "expected %s but got %s for row %i of plane %s with %s" % (hexstr(in_rowdata), hexstr(out_rowdata), y, plane, encoding))
def get_response_salt(self, client_salt=None): server_salt = self.salt #make sure it does not get re-used: self.salt = None if client_salt is None: return server_salt salt = gendigest(self.salt_digest, client_salt, server_salt) if salt in SysAuthenticator.USED_SALT: raise Exception("danger: an attempt was made to re-use the same computed salt") log("combined salt(%s, %s)=%s", hexstr(server_salt), hexstr(client_salt), hexstr(salt)) SysAuthenticator.USED_SALT.append(salt) return salt
def do_test_backend(self, message=b"some message1234", encrypt_count=1, decrypt_count=1): def mustequ(l): if not l: return v = l[0] size = len(l) for i in range(size): assert l[i] == v password = "******" key_salt = DEFAULT_SALT key_hash = DEFAULT_KEY_HASH iterations = DEFAULT_ITERATIONS block_size = DEFAULT_KEYSIZE #test key stretching: args = password, key_salt, key_hash, block_size, iterations secret = self.backend.get_key(*args) log("%s%s=%s" % (self.backend.get_key, args, hexstr(secret))) assert secret is not None #test creation of encryptors and decryptors: iv = DEFAULT_IV args = secret, iv enc = self.backend.get_encryptor(*args) log("%s%s=%s" % (self.backend.get_encryptor, args, enc)) assert enc is not None dec = self.backend.get_decryptor(*args) log("%s%s=%s" % (self.backend.get_decryptor, args, dec)) assert dec is not None #print("init took %ims", (monotonic()-start)//1000) #test encoding of a message: encrypted = [] for i in range(encrypt_count): v = enc.encrypt(message) #print("%s%s=%s" % (enc.encrypt, (message,), hexstr(v))) assert v is not None if i == 0: encrypted.append(v) mustequ(encrypted) #test decoding of the message: decrypted = [] for i in range(decrypt_count): v = dec.decrypt(encrypted[0]) log("%s%s=%s" % (dec.decrypt, (encrypted[0], ), hexstr(v))) assert v is not None if i == 0: decrypted.append(v) mustequ(decrypted) if decrypted: mustequ([decrypted[0], message])
def send(self, packet): if self._closed: log("connection is closed already, not sending packet") return if log.is_debug_enabled(): if len(packet)<=16: log("send(%i bytes: %s)", len(packet), hexstr(packet)) else: from xpra.simple_stats import std_unit log("send(%sBytes: %s..)", std_unit(len(packet)), hexstr(packet[:16])) if self._write_thread is None: self.start_write_thread() self._write_queue.put(packet)
def got_contents(dtype, dformat, data): 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 not data): no_contents() return log("perform clipboard limit checking - datasize - %d, %d", len(data), self.max_clipboard_send_size) truncated = 0 if self.max_clipboard_send_size > 0: max_send_datalen = self.max_clipboard_send_size * 8 // get_format_size( dformat) if len(data) > max_send_datalen: truncated = len(data) - max_send_datalen data = data[:max_send_datalen] munged = self._munge_raw_selection_to_wire(target, dtype, dformat, data) wire_encoding, wire_data = munged log("clipboard raw -> wire: %r -> %r", (dtype, dformat, data), munged) if wire_encoding is None: no_contents() return wire_data = self._may_compress(dtype, dformat, wire_data) if wire_data is not None: packet = [ "clipboard-contents", request_id, selection, dtype, dformat, wire_encoding, wire_data ] if self.clipboard_contents_slice_fix: #sending the extra argument requires the fix packet.append(truncated) self.send(*packet)
def set_icc_profile(self): 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 = ui_clients[0].icc data = None for x in ("data", "icc-data", "icc-profile"): if x in icc: data = icc.get(x) 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
def gendigest(digest, password, salt): assert password and salt salt = memoryview_to_bytes(salt) password = strtobytes(password) if digest=="des": from xpra.net.d3des import generate_response #pylint: disable=import-outside-toplevel password = password.ljust(8, b"\x00")[:8] salt = salt.ljust(16, b"\x00")[:16] v = generate_response(password, salt) return hexstr(v) if digest in ("xor", "kerberos", "gss"): #kerberos and gss use xor because we need to use the actual token #at the other end salt = salt.ljust(len(password), b"\x00")[:len(password)] from xpra.buffers.cyxor import xor_str #@UnresolvedImport pylint: disable=import-outside-toplevel v = xor_str(password, salt) return memoryview_to_bytes(v) digestmod = get_digest_module(digest) if not digestmod: log("invalid digest module '%s'", digest) return None #warn_server_and_exit(EXIT_UNSUPPORTED, # "server requested digest '%s' but it is not supported" % digest, "invalid digest") v = hmac.HMAC(password, salt, digestmod=digestmod).hexdigest() return v
def open_only(data, types=("png", "jpeg", "webp")): itype = get_image_type(data) if itype not in types: raise Exception("invalid data: %s, not recognized as %s, header: %s" % ((itype or "unknown"), csv(types), hexstr(data[:64]))) buf = BytesIO(data) return Image.open(buf)
def _copy_loop(self, log_name, from_conn, to_conn): #log("XpraProxy._copy_loop(%s, %s, %s)", log_name, from_conn, to_conn) try: while not self._closed: log("%s: waiting for data", log_name) buf = untilConcludes(self.is_active, noretry, from_conn.read, PROXY_BUFFER_SIZE) if not buf: log("%s: connection lost", log_name) return if SHOW_DATA: log("%s: %s bytes: %s", log_name, len(buf), repr_ellipsized(buf)) log("%s: %s", log_name, repr_ellipsized(hexstr(buf))) while buf and not self._closed: log("%s: writing %s bytes", log_name, len(buf)) written = untilConcludes(self.is_active, noretry, to_conn.write, buf) buf = buf[written:] log("%s: written %s bytes", log_name, written) log("%s copy loop ended", log_name) except Exception as e: log("%s: %s", log_name, e) finally: self.quit()
def proxy_got_contents(self, request_id, selection, target, dtype, dformat, data): def no_contents(): self.send("clipboard-contents-none", request_id, selection) dtype = bytestostr(dtype) if is_debug_enabled("clipboard"): log("proxy_got_contents(%s, %s, %s, %s, %s, %s:%s) data=0x%s..", request_id, selection, target, dtype, dformat, type(data), len(data or ""), hexstr((data or "")[:200])) if dtype is None or data is None or (dformat==0 and not data): no_contents() return truncated = 0 if self.max_clipboard_send_size > 0: log("perform clipboard limit checking - datasize - %d, %d", len(data), self.max_clipboard_send_size) max_send_datalen = self.max_clipboard_send_size * 8 // get_format_size(dformat) if len(data) > max_send_datalen: truncated = len(data) - max_send_datalen data = data[:max_send_datalen] munged = self._munge_raw_selection_to_wire(target, dtype, dformat, data) if is_debug_enabled("clipboard"): log("clipboard raw -> wire: %r -> %r", (dtype, dformat, ellipsizer(data)), ellipsizer(munged)) wire_encoding, wire_data = munged if wire_encoding is None: no_contents() return wire_data = self._may_compress(dtype, dformat, wire_data) if wire_data is not None: packet = ["clipboard-contents", request_id, selection, dtype, dformat, wire_encoding, wire_data, truncated] self.send(*packet)
def peek_connection(conn, timeout=PEEK_TIMEOUT_MS, size=PEEK_SIZE): log = get_network_logger() log("peek_connection(%s, %i)", conn, timeout) peek_data = b"" start = monotonic_time() elapsed = 0 set_socket_timeout(conn, PEEK_TIMEOUT_MS * 1000) while elapsed <= timeout: try: peek_data = conn.peek(size) if peek_data: break except OSError: log("peek_connection(%s, %i) failed", conn, timeout, exc_info=True) except ValueError: log("peek_connection(%s, %i) failed", conn, timeout, exc_info=True) break sleep(timeout / 4000.0) elapsed = int(1000 * (monotonic_time() - start)) log("peek: elapsed=%s, timeout=%s", elapsed, timeout) line1 = b"" log("socket %s peek: got %i bytes", conn, len(peek_data)) if peek_data: line1 = peek_data.splitlines()[0] log("socket peek=%s", ellipsizer(peek_data, limit=512)) log("socket peek hex=%s", hexstr(peek_data[:128])) log("socket peek line1=%s", ellipsizer(line1)) return peek_data, line1
def _test_csc(self, mod, width=16, height=16, in_csc="BGRX", out_csc="YUV420P", pixel="00000000", expected=()): csc_mod = loader.load_codec(mod) if not csc_mod: print("%s not found" % mod) return if in_csc not in csc_mod.get_input_colorspaces(): raise Exception("%s does not support %s as input" % (mod, in_csc)) if out_csc not in csc_mod.get_output_colorspaces(in_csc): raise Exception("%s does not support %s as output for %s" % (mod, out_csc, in_csc)) csc = csc_mod.ColorspaceConverter() csc.init_context(width, height, in_csc, width, height, out_csc) image = make_test_image(in_csc, width, height) size = image.get_rowstride()//4*image.get_height() bgrx = h2b(pixel)*size image.set_pixels(bgrx) out_image = csc.convert_image(image) csc.clean() assert out_image.get_planes()>=len(expected) #now verify the value for each plane specified: for i, v_str in enumerate(expected): plane = out_image.get_pixels()[i] #plane_stride = out_image.get_rowstride()[i] #assert len(plane)>=plane_stride*out_image.get_height() plane_bytes = memoryview_to_bytes(plane) v = h2b(v_str) if not cmpp(plane_bytes, v): raise Exception("%s: plane %s, expected %s but got %s" % ( mod, out_csc[i], v_str, hexstr(plane_bytes[:len(v)])))
def authenticate_hmac(self, challenge_response, client_salt=None): log("authenticate_hmac(%r, %r)", challenge_response, client_salt) self.sessions = None if not self.salt: log.error( "Error: illegal challenge response received - salt cleared or unset" ) return None #ensure this salt does not get re-used: salt = self.get_response_salt(client_salt) entry = self.get_auth_info() if entry is None: log.warn("Warning: authentication failed") log.warn(" no password for '%s' in '%s'", self.username, self.password_filename) return None log("authenticate: auth-info(%s)=%s", self.username, entry) fpassword, uid, gid, displays, env_options, session_options = entry log("multifile authenticate_hmac password='******', hex(salt)=%s", fpassword, hexstr(salt)) if not verify_digest(self.digest, fpassword, salt, challenge_response): log.warn("Warning: %s challenge for '%s' does not match", self.digest, self.username) return False self.sessions = uid, gid, displays, env_options, session_options return True
def send_hello(self, challenge_response=None, client_salt=None): if not self._protocol: log("send_hello(..) skipped, no protocol (listen mode?)") return try: hello = self.make_hello_base() if self.has_password() and not challenge_response: #avoid sending the full hello: tell the server we want #a packet challenge first hello["challenge"] = True else: hello.update(self.make_hello()) except InitExit as e: log.error("error preparing connection:") log.error(" %s", e) self.quit(EXIT_INTERNAL_ERROR) return except Exception as e: log.error("error preparing connection: %s", e, exc_info=True) self.quit(EXIT_INTERNAL_ERROR) return if challenge_response: hello["challenge_response"] = challenge_response #make it harder for a passive attacker to guess the password length #by observing packet sizes (only relevant for wss and ssl) hello["challenge_padding"] = get_salt( max(32, 512 - len(challenge_response))) if client_salt: hello["challenge_client_salt"] = client_salt log("send_hello(%s) packet=%s", hexstr(challenge_response or ""), hello) self.send("hello", hello)
def invalid_header(self, _proto, data, msg="invalid packet header"): self._packet_parser = self._parse_invalid err = "%s: '%s'" % (msg, hexstr(data[:8])) if len(data) > 1: err += " read buffer=%s (%i bytes)" % (repr_ellipsized(data), len(data)) self.invalid(err, data)
def peek_connection(conn, timeout=PEEK_TIMEOUT_MS): log = get_network_logger() log("peek_connection(%s, %i)", conn, timeout) PEEK_SIZE = 8192 start = monotonic_time() peek_data = b"" while not peek_data and int(1000*(monotonic_time()-start))<timeout: try: peek_data = conn.peek(PEEK_SIZE) except (OSError, IOError): pass except ValueError: log("peek_connection(%s, %i) failed", conn, timeout, exc_info=True) break if not peek_data: sleep(timeout/4000.0) line1 = b"" log("socket %s peek: got %i bytes", conn, len(peek_data)) set_socket_timeout(conn, PEEK_TIMEOUT_MS*1000) if peek_data: line1 = peek_data.splitlines()[0] log("socket peek=%s", repr_ellipsized(peek_data, limit=512)) log("socket peek hex=%s", hexstr(peek_data[:128])) log("socket peek line1=%s", repr_ellipsized(bytestostr(line1))) return peek_data, line1
def get_workarea(): if not is_Wayland(): try: d = get_current_desktop() if d < 0: return None workarea = _get_X11_root_property("_NET_WORKAREA", "CARDINAL") if not workarea: return None screenlog("get_workarea() _NET_WORKAREA=%s (%s), len=%s", ellipsizer(workarea), type(workarea), len(workarea)) #workarea comes as a list of 4 CARDINAL dimensions (x,y,w,h), one for each desktop sizeof_long = struct.calcsize(b"@L") if len(workarea) < (d + 1) * 4 * sizeof_long: screenlog.warn("get_workarea() invalid _NET_WORKAREA value") else: cur_workarea = workarea[d * 4 * sizeof_long:(d + 1) * 4 * sizeof_long] v = struct.unpack(b"@LLLL", cur_workarea) screenlog("get_workarea() %s=%s", hexstr(cur_workarea), v) return v except Exception as e: screenlog("get_workarea()", exc_info=True) screenlog.warn("Warning: failed to query workarea: %s", e) return None
def selftest(full=False): global ENCODINGS from xpra.os_util import hexstr from xpra.codecs.codec_checks import make_test_image img = make_test_image("BGRA", 32, 32) if full: vrange = (0, 50, 100) else: vrange = (50, ) for encoding in tuple(ENCODINGS): try: for q in vrange: for s in vrange: for alpha in (True, False): v = encode(encoding, img, q, s, False, alpha) assert v, "encode output was empty!" cdata = v[1].data log("encode(%s)=%s", (encoding, img, q, s, alpha), hexstr(cdata)) except Exception as e: # pragma: no cover l = log.warn l("Pillow error saving %s with quality=%s, speed=%s, alpha=%s", encoding, q, s, alpha) l(" %s", e, exc_info=True) ENCODINGS.remove(encoding)
def __init__(self, _disp, data): #some applications use the wrong size (ie: blender uses 16) so pad it: sizeof_long = struct.calcsize(b"@L") pdata = _force_length("_MOTIF_WM_HINTS", data, sizeof_long*5, sizeof_long*4) self.flags, self.functions, self.decorations, self.input_mode, self.status = \ struct.unpack(b"@LLLlL", pdata) log("MotifWMHints(%s)=%s", hexstr(data), self)
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 "")) self.icc_profile = data from xpra.x11.gtk_x11.prop import prop_set prop_set(self.root_window, "_ICC_PROFILE", ["u32"], [ord(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
def get_icc_info(self) -> dict: icc_info = { "sync": SYNC_ICC, } if SYNC_ICC: icc_info["profile"] = hexstr(self.icc_profile) return icc_info
def get_settings(d): global XSETTINGS_CACHE DEBUG_XSETTINGS = envbool("XPRA_XSETTINGS_DEBUG", False) #parse xsettings according to #http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html assert len(d)>=12, "_XSETTINGS_SETTINGS property is too small: %s" % len(d) if DEBUG_XSETTINGS: log("get_settings(%s)", tuple(d)) byte_order, _, _, _, serial, n_settings = struct.unpack(b"=BBBBII", d[:12]) cache = XSETTINGS_CACHE log("get_settings(..) found byte_order=%s (local is %s), serial=%s, n_settings=%s, cache=%s", byte_order, get_local_byteorder(), serial, n_settings, cache) if cache and cache[0]==serial: log("get_settings(..) returning value from cache") return cache settings = [] pos = 12 while n_settings>len(settings): log("get_settings(..) pos=%i (len=%i), data=%s", pos, len(d), hexstr(d[pos:])) istart = pos #parse header: setting_type, _, name_len = struct.unpack(b"=BBH", d[pos:pos+4]) pos += 4 #extract property name: prop_name = d[pos:pos+name_len] pos += (name_len + 0x3) & ~0x3 #serial: assert len(d)>=pos+4, "not enough data (%s bytes) to extract serial (4 bytes needed)" % (len(d)-pos) last_change_serial = struct.unpack(b"=I", d[pos:pos+4])[0] pos += 4 if DEBUG_XSETTINGS: log("get_settings(..) found property %s of type %s, serial=%s", prop_name, XSettingsNames.get(setting_type, "INVALID!"), last_change_serial) #extract value: if setting_type==XSettingsTypeInteger: assert len(d)>=pos+4, "not enough data (%s bytes) to extract int (4 bytes needed)" % (len(d)-pos) value = int(struct.unpack(b"=I", d[pos:pos+4])[0]) pos += 4 elif setting_type==XSettingsTypeString: assert len(d)>=pos+4, "not enough data (%s bytes) to extract string length (4 bytes needed)" % (len(d)-pos) value_len = struct.unpack(b"=I", d[pos:pos+4])[0] assert len(d)>=pos+4+value_len, "not enough data (%s bytes) to extract string (%s bytes needed)" % (len(d)-pos-4, value_len) value = d[pos+4:pos+4+value_len] pos += 4 + ((value_len + 0x3) & ~0x3) elif setting_type==XSettingsTypeColor: assert len(d)>=pos+8, "not enough data (%s bytes) to extract color (8 bytes needed)" % (len(d)-pos) red, blue, green, alpha = struct.unpack(b"=HHHH", d[pos:pos+8]) value = (red, blue, green, alpha) pos += 8 else: log.error("invalid setting type: %s, cannot continue parsing XSETTINGS!", setting_type) break setting = setting_type, prop_name, value, last_change_serial if DEBUG_XSETTINGS: log("get_settings(..) %s -> %s", tuple(d[istart:pos]), setting) settings.append(setting) log("get_settings(..) settings=%s", settings) XSETTINGS_CACHE = (serial, settings) return serial, settings
def fixvalue(w): if isinstance(w, bytes): if k.endswith(".data"): return hexstr(w) return u(w) elif isinstance(w, (tuple,list)): return type(w)([fixvalue(x) for x in w]) return w
def send(self, packet): log("send(%i bytes: %s..)", len(packet), hexstr(packet[:16])) if self._closed: log("connection is closed already, not sending packet") return if self._write_thread is None: self.start_write_thread() self._write_queue.put(packet)