def send_data_request(self, action, dtype, url, mimetype="", data="", filesize=0, printit=False, openit=True, options=None): send_id = uuid.uuid4().hex if len(self.pending_send_data) >= MAX_CONCURRENT_FILES: filelog.warn("Warning: %s dropped", action) filelog.warn(" %i transfer%s already waiting for a response", len(self.pending_send_data), engs(self.pending_send_data)) return None self.pending_send_data[send_id] = (dtype, url, mimetype, data, filesize, printit, openit, options or {}) delay = self.remote_file_ask_timeout * 1000 self.pending_send_data_timers[send_id] = self.timeout_add( delay, self.send_data_ask_timeout, send_id) filelog("sending data request for %s '%s' with send-id=%s", u(dtype), u(url), send_id) self.send("send-data-request", dtype, send_id, url, mimetype, filesize, printit, openit, options or {}) return send_id
def do_send_file(self, filename, mimetype, data, filesize=0, printit=False, openit=False, options=None, send_id=""): if printit: action = "print" l = printlog else: action = "upload" l = filelog l("do_send_file%s", (u(filename), mimetype, type(data), "%i bytes" % filesize, printit, openit, options)) if not self.check_file_size(action, filename, filesize): return False h = hashlib.sha256() h.update(data) absfile = os.path.abspath(filename) filelog("sha256 digest('%s')=%s", u(absfile), h.hexdigest()) options = options or {} options["sha256"] = h.hexdigest() chunk_size = min(self.file_chunks, self.remote_file_chunks) if 0 < chunk_size < filesize: if len(self.send_chunks_in_progress) >= MAX_CONCURRENT_FILES: raise Exception("too many file transfers in progress: %i" % len(self.send_chunks_in_progress)) #chunking is supported and the file is big enough chunk_id = uuid.uuid4().hex options["file-chunk-id"] = chunk_id #timer to check that the other end is requesting more chunks: timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_sending, chunk_id, 0) chunk_state = [monotonic(), data, chunk_size, timer, 0] self.send_chunks_in_progress[chunk_id] = chunk_state cdata = "" filelog( "using chunks, sending initial file-chunk-id=%s, for chunk size=%s", chunk_id, chunk_size) else: #send everything now: cdata = self.compressed_wrapper("file-data", data) assert len( cdata) <= filesize #compressed wrapper ensures this is true filelog("sending full file: %i bytes (chunk size=%i)", filesize, chunk_size) basefilename = os.path.basename(filename) self.send("send-file", basefilename, mimetype, printit, openit, filesize, cdata, options, send_id) return True
def client_upgrade(read, write, host, port, path=""): request = "GET /%s HTTP/1.1" % path log("client_upgrade: http request: %s", request) lines = [request.encode("latin1")] key = b64encode(uuid.uuid4().bytes) headers = get_headers(host, port) headers[b"Sec-WebSocket-Key"] = key for k, v in headers.items(): lines.append(b"%s: %s" % (k, v)) lines.append(b"") lines.append(b"") http_request = b"\r\n".join(lines) log("client_upgrade: sending http headers: %s", headers) now = monotonic() while http_request and monotonic() - now < MAX_WRITE_TIME: w = write(http_request) http_request = http_request[w:] now = monotonic() response = b"" while ("sec-websocket-protocol" not in u(response).lower()) and monotonic() - now < MAX_READ_TIME: response += read(READ_CHUNK_SIZE) headers = parse_response_header(response) verify_response_headers(headers, key) log("client_upgrade: done")
def get_wm_name(): wm_name = os.environ.get("XDG_CURRENT_DESKTOP", "") or os.environ.get( "XDG_SESSION_DESKTOP") or os.environ.get("DESKTOP_SESSION") if os.environ.get("XDG_SESSION_TYPE") == "wayland" or os.environ.get( "GDK_BACKEND") == "wayland": if wm_name: wm_name += " on wayland" else: wm_name = "wayland" elif is_X11(): try: wm_check = _get_X11_root_property("_NET_SUPPORTING_WM_CHECK", "WINDOW") if wm_check: xid = struct.unpack(b"@L", wm_check)[0] traylog("_NET_SUPPORTING_WM_CHECK window=%#x", xid) wm_name = _get_X11_window_property(xid, "_NET_WM_NAME", "UTF8_STRING") traylog("_NET_WM_NAME=%s", wm_name) if wm_name: return u(wm_name) except Exception as e: traylog("get_wm_name()", exc_info=True) traylog.error("Error accessing window manager information:") traylog.error(" %s", e) return wm_name
def cancel_download(self, send_id, message="Cancelled"): filelog("cancel_download(%s, %s)", send_id, message) for chunk_id, chunk_state in dict(self.receive_chunks_in_progress).items(): if chunk_state[-3]==send_id: self.cancel_file(chunk_id, message) return filelog.error("Error: cannot cancel download %s, entry not found!", u(send_id))
def _process_request_file(self, proto, packet): ss = self.get_server_source(proto) if not ss: printlog.warn("Warning: invalid client source for send-data-response packet") return argf = u(packet[1]) openit = packet[2] filename = os.path.abspath(osexpand(argf)) if not os.path.exists(filename): filelog.warn("Warning: the file requested does not exist:") filelog.warn(" %s", filename) ss.may_notify(XPRA_FILETRANSFER_NOTIFICATION_ID, "File not found", "The file requested does not exist:\n%s" % filename, icon_name="file") return try: stat = os.stat(filename) filelog("os.stat(%s)=%s", filename, stat) except os.error: filelog("os.stat(%s)", filename, exc_info=True) else: file_size = stat.st_size if file_size>self.file_transfer.file_size_limit or file_size>ss.file_size_limit: ss.may_notify(XPRA_FILETRANSFER_NOTIFICATION_ID, "File too large", "The file requested is too large to send:\n%s\nis %s" % (argf, std_unit(file_size)), icon_name="file") return data = load_binary_file(filename) ss.send_file(filename, "", data, len(data), openit=openit, options={"request-file" : (argf, openit)})
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 action_button(self, action_id, action_text): button = Gtk.Button(u(action_text)) button.set_relief(Gtk.ReliefStyle.NORMAL) def popup_cb_clicked(*args): self.hide_notification() log("popup_cb_clicked%s for action_id=%s, action_text=%s", args, action_id, action_text) self.action_cb(self.nid, action_id) button.connect("clicked", popup_cb_clicked) return button
def _process_start_command(self, proto, packet): log("start new command: %s", packet) if not self.start_new_commands: log.warn("Warning: received start-command request,") log.warn(" but the feature is currently disabled") return name, command, ignore = packet[1:4] if isinstance(command, (list, tuple)): cmd = command else: cmd = u(command) proc = self.start_command(u(name), cmd, ignore) if len(packet)>=5: shared = packet[4] if proc and not shared: ss = self.get_server_source(proto) assert ss log("adding filter: pid=%s for %s", proc.pid, proto) ss.add_window_filter("window", "pid", "=", proc.pid) log("process_start_command: proc=%s", proc)
def get_window_info(self, wi): #version info: geom = wi.inttupleget("geometry") g_str = "%ix%i at %i,%i" % (geom[2], geom[3], geom[0], geom[1]) sc = wi.dictget("size-constraints") if sc: def sc_str(k, v): k = bytestostr(k) if k == "gravity": v = GRAVITY_STR.get(v, v) return "%s=%s" % (k, str(v)) g_str += " - %s" % csv(sc_str(k, v) for k, v in sc.items()) line1 = "" pid = wi.intget("pid", 0) if pid: line1 = "pid %i: " % pid title = wi.get("title", "") if not isinstance(title, str): title = u(strtobytes(title)) if title: line1 += ' "%s"' % title attrs = [ x for x in ( "above", "below", "bypass-compositor", "focused", "fullscreen", "grabbed", "iconic", "maximized", "modal", "override-redirect", "shaded", "skip-pager", "skip-taskbar", "sticky", "tray", ) if wi.boolget(x) ] if not wi.boolget("shown"): attrs.insert(0, "hidden") wtype = wi.strtupleget("window-type", ("NORMAL", )) tinfo = " - ".join(csv(x) for x in (wtype, attrs) if x) info = [] if line1: info.append(line1) info += [g_str, tinfo] return tuple((x, WHITE) for x in info if x)
def do_process_send_data_request(self, dtype, send_id, url, _, filesize, printit, openit, options): filelog( "do_process_send_data_request: send_id=%s, url=%s, printit=%s, openit=%s, options=%s", u(send_id), url, printit, openit, options) def cb_answer(accept): filelog("accept%s=%s", (url, printit, openit), accept) self.send("send-data-response", send_id, accept) #could be a request we made: #(in which case we can just accept it without prompt) rf = options.tupleget("request-file") if rf and len(rf) >= 2: argf, openit = rf[:2] openit = self.files_requested.pop(bytestostr(argf), None) if openit is not None: self.files_accepted[send_id] = openit cb_answer(True) return if dtype == "file": if not self.file_transfer: cb_answer(False) return url = os.path.basename(url) if printit: ask = self.printing_ask elif openit: ask = self.file_transfer_ask or self.open_files_ask else: ask = self.file_transfer_ask elif dtype == "url": if not self.open_url: cb_answer(False) return ask = self.open_url_ask else: filelog.warn("Warning: unknown data request type '%s'", dtype) cb_answer(False) return if not ask: filelog.warn("Warning: received a send-data request for a %s,", dtype) filelog.warn(" but authorization is not required by the client") #fail it because if we responded with True, #it would fail later when we don't find this send_id in our accepted list cb_answer(False) else: self.ask_data_request(cb_answer, send_id, dtype, url, filesize, printit, openit)
def get_x11_wm_name(): if not is_X11(): return None try: wm_check = _get_X11_root_property("_NET_SUPPORTING_WM_CHECK", "WINDOW") if wm_check: xid = struct.unpack(b"@L", wm_check)[0] traylog("_NET_SUPPORTING_WM_CHECK window=%#x", xid) wm_name = _get_X11_window_property(xid, "_NET_WM_NAME", "UTF8_STRING") traylog("_NET_WM_NAME=%s", wm_name) if wm_name: return u(wm_name) except Exception as e: screenlog("get_x11_wm_name()", exc_info=True) screenlog.error("Error accessing window manager information:") screenlog.error(" %s", e) return None
def _process_print(self, _proto, packet): #ie: from the xpraforwarder we call this command: #command = ["xpra", "print", "socket:/path/tosocket", # filename, mimetype, source, title, printer, no_copies, print_options] assert self.file_transfer.printing #printlog("_process_print(%s, %s)", proto, packet) if len(packet)<3: printlog.error("Error: invalid print packet, only %i arguments", len(packet)) printlog.error(" %s", [repr_ellipsized(x) for x in packet]) return filename = u(packet[1]) file_data = packet[2] mimetype, source_uuid, title, printer, no_copies, print_options = "", "*", "unnamed document", "", 1, "" if len(packet)>=4: mimetype = bytestostr(packet[3]) if len(packet)>=5: source_uuid = bytestostr(packet[4]) if len(packet)>=6: title = u(packet[5]) if len(packet)>=7: printer = bytestostr(packet[6]) if len(packet)>=8: no_copies = int(packet[7]) if len(packet)>=9: print_options = packet[8] #parse and validate: if len(mimetype)>=128: printlog.error("Error: invalid mimetype in print packet:") printlog.error(" %s", repr_ellipsized(mimetype)) return if not isinstance(print_options, dict): s = bytestostr(print_options) print_options = {} for x in s.split(" "): parts = x.split("=", 1) if len(parts)==2: print_options[parts[0]] = parts[1] printlog("process_print: %s", (filename, mimetype, "%s bytes" % len(file_data), source_uuid, title, printer, no_copies, print_options)) printlog("process_print: got %s bytes for file %s", len(file_data), filename) #parse the print options: hu = hashlib.sha256() hu.update(file_data) printlog("sha1 digest: %s", hu.hexdigest()) options = { "printer" : printer, "title" : title, "copies" : no_copies, "options" : print_options, "sha256" : hu.hexdigest(), } printlog("parsed printer options: %s", options) if SAVE_PRINT_JOBS: self._save_print_job(filename, file_data) sent = 0 sources = tuple(self._server_sources.values()) printlog("will try to send to %i clients: %s", len(sources), sources) for ss in sources: if source_uuid not in ("*", ss.uuid): printlog("not sending to %s (uuid=%s, wanted uuid=%s)", ss, ss.uuid, source_uuid) continue if not ss.printing: if source_uuid!='*': printlog.warn("Warning: printing is not enabled for:") printlog.warn(" %s", ss) else: printlog("printing is not enabled for %s", ss) continue if not ss.printers: printlog.warn("Warning: client %s does not have any printers", ss.uuid) continue if printer not in ss.printers: printlog.warn("Warning: client %s does not have a '%s' printer", ss.uuid, printer) continue printlog("'%s' sent to %s for printing on '%s'", bytestostr(title or filename), ss, printer) if ss.send_file(filename, mimetype, file_data, len(file_data), True, True, options): sent += 1 #warn if not sent: if sent==0: l = printlog.warn else: l = printlog.info unit_str, v = to_std_unit(len(file_data), unit=1024) l("'%s' (%i%sB) sent to %i client%s for printing", title or filename, v, unit_str, sent, engs(sent))