Exemple #1
0
 def _process_send_data_response(self, packet):
     send_id, accept = packet[1:3]
     send_id = net_utf8(send_id)
     filelog("process send-data-response: send_id=%s, accept=%s", send_id, accept)
     timer = self.pending_send_data_timers.pop(send_id, None)
     if timer:
         self.source_remove(timer)
     v = self.pending_send_data.pop(send_id, None)
     if v is None:
         filelog.warn("Warning: cannot find send-file entry")
         return
     dtype = net_utf8(v[0])
     url = net_utf8(v[1])
     if accept==DENY:
         filelog.info("the request to send %s '%s' has been denied", dtype, url)
         return
     assert accept in (ACCEPT, OPEN), "unknown value for send-data response: %s" % (accept,)
     if dtype=="file":
         mimetype, data, filesize, printit, openit, options = v[2:]
         if accept==ACCEPT:
             self.do_send_file(url, mimetype, data, filesize, printit, openit, options, send_id)
         else:
             assert openit and accept==OPEN
             #try to open at this end:
             self._open_file(url)
     elif dtype=="url":
         if accept==ACCEPT:
             self.do_send_open_url(url, send_id)
         else:
             assert accept==OPEN
             #open it at this end:
             self._open_url(url)
     else:
         filelog.error("Error: unknown datatype '%s'", dtype)
Exemple #2
0
 def _process_notify_show(self, packet):
     if not self.notifications_enabled:
         log("process_notify_show: ignoring packet, notifications are disabled")
         return
     self._ui_event()
     dbus_id, nid, app_name, replaces_nid, app_icon, summary, body, expire_timeout = packet[1:9]
     icon, actions, hints = None, [], {}
     if len(packet)>=10:
         icon = packet[9]
     if len(packet)>=12:
         actions, hints = packet[10], packet[11]
     #note: if the server doesn't support notification forwarding,
     #it can still send us the messages (via xpra control or the dbus interface)
     log("_process_notify_show(%s) notifier=%s, server_notifications=%s",
         repr_ellipsized(packet), self.notifier, self.server_notifications)
     log("notification actions=%s, hints=%s", actions, hints)
     assert self.notifier
     #this one of the few places where we actually do care about character encoding:
     summary = net_utf8(summary)
     body = net_utf8(body)
     app_name = net_utf8(app_name)
     tray = self.get_tray_window(app_name, hints)
     log("get_tray_window(%s)=%s", app_name, tray)
     self.notifier.show_notify(dbus_id, tray, nid,
                               app_name, replaces_nid, app_icon,
                               summary, body, actions, hints, expire_timeout, icon)
Exemple #3
0
 def _process_logging(self, proto, packet):
     assert self.remote_logging_receive
     ss = self.get_server_source(proto)
     if ss is None:
         return
     level, msg = packet[1:3]
     prefix = "client "
     counter = getattr(ss, "counter", 0)
     if counter>0:
         prefix += "%3i " % counter
     if len(packet)>=4:
         dtime = packet[3]
         prefix += "@%02i.%03i " % ((dtime//1000)%60, dtime%1000)
     try:
         if isinstance(msg, (tuple, list)):
             dmsg = " ".join(net_utf8(x) for x in msg)
         else:
             dmsg = net_utf8(msg)
         for l in dmsg.splitlines():
             self.do_log(level, prefix+l)
     except Exception as e:
         log("log message decoding error", exc_info=True)
         log.error("Error: failed to parse logging message:")
         log.error(" %s", repr_ellipsized(msg))
         log.error(" %s", e)
Exemple #4
0
 def _process_send_data_request(self, packet):
     dtype, send_id, url, _, filesize, printit, openit = packet[1:8]
     options = {}
     if len(packet)>=9:
         options = packet[8]
     #filenames and url are always sent encoded as utf8:
     url = net_utf8(url)
     dtype = net_utf8(dtype)
     send_id = net_utf8(send_id)
     self.do_process_send_data_request(dtype, send_id, url, _, filesize, printit, openit, typedict(options))
Exemple #5
0
 def _process_open_url(self, packet):
     send_id = net_utf8(packet[2])
     url = net_utf8(packet[1])
     if not self.open_url:
         filelog.warn("Warning: received a request to open URL '%s'", url)
         filelog.warn(" but opening of URLs is disabled")
         return
     if not self.open_url_ask or self.accept_data(send_id, "url", url, False, True):
         self._open_url(url)
     else:
         filelog("url '%s' not accepted", url)
Exemple #6
0
 def getvar(var):
     #"hostname" is magic:
     #we try harder to find a useful value to show:
     if var in ("hostname", "hostinfo"):
         if var == "hostinfo" and getattr(self._client,
                                          "mmap_enabled", False):
             #this is a local connection for sure
             server_display = getattr(self._client,
                                      "server_display", None)
             if server_display:
                 return server_display
         #try to find the hostname:
         proto = getattr(self._client, "_protocol", None)
         if proto:
             conn = getattr(proto, "_conn", None)
             if conn:
                 hostname = conn.info.get("host") or bytestostr(
                     conn.target)
                 if hostname:
                     return hostname
         for m in ("client-machine", "server-machine"):
             value = getvar(m)
             if value not in ("localhost", "localhost.localdomain",
                              "", None):
                 return value
         return UNKNOWN_MACHINE
     value = metadata.get(var) or self._metadata.get(var)
     if value is None:
         return default_values.get(var, "<unknown %s>" % var)
     return net_utf8(value)
 def control_command_send_file(self, filename, openit="open", client_uuids="*", maxbitrate=0):
     #we always get the values as strings from the command interface,
     #but those may actually be utf8 encoded binary strings,
     #so we may have to do an ugly roundtrip:
     filename = net_utf8(filename)
     openit = str(openit).lower() in ("open", "true", "1")
     return self.do_control_file_command("send file", client_uuids, filename, "file_transfer", (False, openit))
Exemple #8
0
 def got_token(self,
               targets,
               target_data=None,
               claim=True,
               synchronous_client=False):
     # the remote end now owns the clipboard
     self.cancel_emit_token()
     if not self._enabled:
         return
     self._got_token_events += 1
     log(
         "got token, selection=%s, targets=%s, target data=%s, claim=%s, synchronous_client=%s, can-receive=%s",
         self._selection, targets, target_data, claim, synchronous_client,
         self._can_receive)
     if claim:
         self._have_token = True
     if not self._can_receive:
         return
     if target_data and claim:
         targets = target_data.keys()
         text_targets = tuple(x for x in targets if x in TEXT_TARGETS)
         for text_target in text_targets:
             dtype, dformat, data = target_data.get(text_target)
             if dformat != 8:
                 continue
             text = net_utf8(data)
             log("setting text data %s / %s of size %i: %s", dtype, dformat,
                 len(text), ellipsizer(text))
             self._block_owner_change = monotonic()
             self.clipboard.set_text(text, len(text))
             return
Exemple #9
0
def udict(d):
    #with rencode, we may get bytes instead of strings:
    t = typedict()
    for k, v in d.items():
        if isinstance(k, bytes):
            k = net_utf8(k)
        t[k] = v
    return t
Exemple #10
0
 def _process_ack_file_chunk(self, packet):
     #the other end received our send-file or send-file-chunk,
     #send some more file data
     filelog("ack-file-chunk: %s", packet[1:])
     chunk_id, state, error_message, chunk = packet[1:5]
     chunk_id = net_utf8(chunk_id)
     if not state:
         filelog.info("the remote end is cancelling the file transfer:")
         filelog.info(" %s", net_utf8(error_message))
         self.cancel_sending(chunk_id)
         return
     chunk_state = self.send_chunks_in_progress.get(chunk_id)
     if not chunk_state:
         filelog.error("Error: cannot find the file transfer id '%r'",
                       chunk_id)
         return
     if chunk_state[-1] != chunk:
         filelog.error("Error: chunk number mismatch (%i vs %i)",
                       chunk_state, chunk)
         self.cancel_sending(chunk_id)
         return
     start_time, data, chunk_size, timer, chunk = chunk_state
     if not data:
         #all sent!
         elapsed = monotonic() - start_time
         filelog("%i chunks of %i bytes sent in %ims (%sB/s)", chunk,
                 chunk_size, elapsed * 1000,
                 std_unit(chunk * chunk_size / elapsed))
         self.cancel_sending(chunk_id)
         return
     assert chunk_size > 0
     #carve out another chunk:
     cdata = self.compressed_wrapper("file-data", data[:chunk_size])
     data = data[chunk_size:]
     chunk += 1
     if timer:
         self.source_remove(timer)
     timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_sending,
                              chunk_id, chunk)
     self.send_chunks_in_progress[chunk_id] = [
         start_time, data, chunk_size, timer, chunk
     ]
     self.send("send-file-chunk", chunk_id, chunk, cdata, bool(data))
Exemple #11
0
 def _process_logging(self, packet):
     assert not self.local_logging, "cannot receive logging packets when forwarding logging!"
     level, msg = packet[1:3]
     prefix = "server: "
     if len(packet)>=4:
         dtime = packet[3]
         prefix += "@%02i.%03i " % ((dtime//1000)%60, dtime%1000)
     try:
         if isinstance(msg, (tuple, list)):
             dmsg = " ".join(net_utf8(x) for x in msg)
         else:
             dmsg = net_utf8(msg)
         for l in dmsg.splitlines():
             self.do_log(level, prefix+l)
     except Exception as e:
         log("log message decoding error", exc_info=True)
         log.error("Error: failed to parse logging message:")
         log.error(" %s", repr_ellipsized(msg))
         log.error(" %s", e)
Exemple #12
0
 def _process_key_action(self, proto, packet):
     if self.readonly:
         return
     wid, keyname, pressed, modifiers, keyval, keystr, client_keycode, group = packet[
         1:9]
     ss = self.get_server_source(proto)
     if ss is None:
         return
     keyname = net_utf8(keyname)
     keystr = net_utf8(keystr)
     modifiers = list(net_utf8(x) for x in modifiers)
     self.set_ui_driver(ss)
     keycode, group = self.get_keycode(ss, client_keycode, keyname, pressed,
                                       modifiers, keyval, keystr, group)
     keylog("process_key_action(%s) server keycode=%s, group=%i", packet,
            keycode, group)
     if group >= 0 and keycode >= 0:
         self.set_keyboard_layout_group(group)
     #currently unused: (group, is_modifier) = packet[8:10]
     self._focus(ss, wid, None)
     ss.make_keymask_match(modifiers,
                           keycode,
                           ignored_modifier_keynames=[keyname])
     #negative keycodes are used for key events without a real keypress/unpress
     #for example, used by win32 to send Caps_Lock/Num_Lock changes
     if keycode >= 0:
         try:
             is_mod = ss.is_modifier(keyname, keycode)
             self._handle_key(wid, pressed, keyname, keyval, keycode,
                              modifiers, is_mod, ss.keyboard_config.sync)
         except Exception as e:
             keylog("process_key_action%s", (proto, packet), exc_info=True)
             keylog.error("Error: failed to %s key", ["unpress",
                                                      "press"][pressed])
             keylog.error(" %s", e)
             keylog.error(" for keyname=%s, keyval=%i, keycode=%i", keyname,
                          keyval, keycode)
     ss.user_event()
Exemple #13
0
 def remove_printer(self, name):
     printer = net_utf8(name)
     try:
         self.printers_added.remove(printer)
     except KeyError:
         log("not removing printer '%s' - since we didn't add it", name)
     else:
         try:
             from xpra.platform.pycups_printing import remove_printer
             remove_printer(printer)
             log.info("removed remote printer '%s'", printer)
         except Exception as e:
             log("remove_printer(%s)", printer, exc_info=True)
             log.error("Error: failed to remove printer '%s':", name)
             log.error(" %s", e)
Exemple #14
0
 def _process_shell_reply(self, packet):
     fd = packet[1]
     message = packet[2]
     if fd==1:
         stream = sys.stdout
     elif fd==2:
         stream = sys.stderr
     else:
         raise Exception("invalid file descriptor %i" % fd)
     s = net_utf8(message)
     if s.endswith("\n"):
         s = s[:-1]
     stream.write("%s" % s)
     stream.flush()
     if fd==2:
         stream.write("\n")
         self.print_prompt()
Exemple #15
0
 def got_contents(self, target, dtype=None, dformat=None, data=None):
     #if this is the special target 'TARGETS', cache the result:
     if target=="TARGETS" and dtype=="ATOM" and dformat==32:
         self.targets = _filter_targets(data)
         log("got_contents: tell OS we have %s", csv(self.targets))
         image_types = tuple(t for t in IMAGE_FORMATS if t in self.targets)
         log("image_types=%s, dtype=%s (is text=%s)",
                  image_types, dtype, dtype in TEXT_TARGETS)
         if image_types and dtype not in TEXT_TARGETS:
             #request image:
             self.send_clipboard_request_handler(self, self._selection, image_types[0])
         return
     if dformat==8 and dtype in TEXT_TARGETS:
         log("we got a byte string: %s", data)
         self.set_clipboard_text(net_utf8(data))
     if dformat==8 and dtype in IMAGE_FORMATS:
         log("we got a %s image", dtype)
         self.set_image_data(dtype, data)
Exemple #16
0
 def parse_client_caps(self, c: typedict):
     self.vrefresh = c.intget("vrefresh", -1)
     self.randr_notify = c.boolget("randr_notify")
     self.desktop_size = c.intpair("desktop_size")
     if self.desktop_size is not None:
         w, h = self.desktop_size
         if w <= 0 or h <= 0 or w >= 32768 or h >= 32768:
             log.warn("ignoring invalid desktop dimensions: %sx%s", w, h)
             self.desktop_size = None
     self.desktop_mode_size = c.intpair("desktop_mode_size")
     self.desktop_size_unscaled = c.intpair("desktop_size.unscaled")
     self.screen_resize_bigger = c.boolget("screen-resize-bigger", True)
     self.set_screen_sizes(c.tupleget("screen_sizes"))
     desktop_names = tuple(net_utf8(x) for x in c.tupleget("desktop.names"))
     self.set_desktops(c.intget("desktops", 1), desktop_names)
     self.show_desktop_allowed = c.boolget("show-desktop")
     self.icc = c.dictget("icc", {})
     self.display_icc = c.dictget("display-icc", {})
     self.opengl_props = c.dictget("opengl", {})
Exemple #17
0
 def got_contents(self, target, dtype=None, dformat=None, data=None):
     #if this is the special target 'TARGETS', cache the result:
     if target == "TARGETS" and dtype == "ATOM" and dformat == 32:
         self.targets = _filter_targets(data)
         #TODO: tell system what targets we have
         log("got_contents: tell OS we have %s", csv(self.targets))
         image_formats = tuple(x for x in ("image/png", "image/jpeg")
                               if x in self.targets)
         if image_formats:
             #request it:
             self.send_clipboard_request_handler(self, self._selection,
                                                 image_formats[0])
     elif dformat == 8 and dtype in TEXT_TARGETS:
         log("we got a byte string: %s", data)
         self.set_clipboard_text(net_utf8(data))
     elif dformat == 8 and dtype.startswith("image/"):
         img_format = dtype.split("/")[-1]  #ie: 'png'
         self.set_clipboard_image(img_format, data)
     else:
         log("no handling: target=%s, dtype=%s, dformat=%s, data=%s",
             target, dtype, dformat, ellipsizer(data))
Exemple #18
0
 def set_desktops(self, desktops, desktop_names):
     self.desktops = desktops or 1
     self.desktop_names = tuple(net_utf8(d) for d in (desktop_names or ()))
Exemple #19
0
    def set_metadata(self, metadata):
        metalog("set_metadata(%s)", metadata)
        debug_props = [x for x in PROPERTIES_DEBUG if x in metadata.keys()]
        for x in debug_props:
            metalog.info("set_metadata: %s=%s", x, metadata.get(x))
        #WARNING: "class-instance" needs to go first because others may realize the window
        #(and GTK doesn't set the "class-instance" once the window is realized)
        if "class-instance" in metadata:
            self.set_class_instance(
                *self._metadata.strtupleget("class-instance", ("xpra",
                                                               "Xpra"), 2, 2))
            self.reset_icon()

        if "title" in metadata:
            title = self._get_window_title(metadata)
            self.set_title(title)

        if "icon-title" in metadata:
            icon_title = metadata.strget("icon-title", "")
            self.set_icon_name(net_utf8(icon_title))
            #the DE may have reset the icon now,
            #force it to use the one we really want:
            self.reset_icon()

        if "size-constraints" in metadata:
            sc = typedict(metadata.dictget("size-constraints", {}))
            self.size_constraints = sc
            self._set_initial_position = sc.boolget("set-initial-position",
                                                    self._set_initial_position)
            self.set_size_constraints(sc, self.max_window_size)

        if "set-initial-position" in metadata:
            #this should be redundant - but we keep it here for consistency
            self._set_initial_position = metadata.boolget(
                "set-initial-position")

        if "transient-for" in metadata:
            wid = metadata.intget("transient-for", -1)
            self.apply_transient_for(wid)

        if "modal" in metadata:
            modal = metadata.boolget("modal")
            self.set_modal(modal)

        #apply window-type hint if window has not been mapped yet:
        if "window-type" in metadata and not self.get_mapped():
            window_types = metadata.strtupleget("window-type")
            self.set_window_type(window_types)

        if "role" in metadata:
            role = metadata.strget("role")
            self.set_role(role)

        if "xid" in metadata:
            xid = metadata.strget("xid")
            self.set_xid(xid)

        if "opacity" in metadata:
            opacity = metadata.intget("opacity", -1)
            if opacity < 0:
                opacity = 1
            else:
                opacity = min(1, opacity / 0xffffffff)
            #requires gtk>=2.12!
            if hasattr(self, "set_opacity"):
                self.set_opacity(opacity)

        if "has-alpha" in metadata:
            new_alpha = metadata.boolget("has-alpha")
            if new_alpha != self._has_alpha:
                l = alphalog
                if not WIN32:
                    #win32 without opengl can't do transparency,
                    #so it triggers too many warnings
                    l = log.warn
                l("Warning: window %#x changed its transparency attribute",
                  self._id)
                l(" from %s to %s, behaviour is undefined", self._has_alpha,
                  new_alpha)
                self._has_alpha = new_alpha

        if "maximized" in metadata:
            maximized = metadata.boolget("maximized")
            if maximized != self._maximized:
                self._maximized = maximized
                if maximized:
                    self.maximize()
                else:
                    self.unmaximize()

        if "fullscreen" in metadata:
            fullscreen = metadata.boolget("fullscreen")
            if self._fullscreen is None or self._fullscreen != fullscreen:
                self._fullscreen = fullscreen
                self.set_fullscreen(fullscreen)

        if "iconic" in metadata:
            iconified = metadata.boolget("iconic")
            if self._iconified != iconified:
                self._iconified = iconified
                if iconified:
                    self.iconify()
                else:
                    self.deiconify()

        if "decorations" in metadata:
            decorated = metadata.boolget("decorations", True)
            was_decorated = self.get_decorated()
            if WIN32 and decorated != was_decorated:
                log.info(
                    "decorations flag toggled, now %s, re-initializing window",
                    decorated)
                self.idle_add(self._client.reinit_window, self._id, self)
            else:
                self.set_decorated(metadata.boolget("decorations"))
                self.apply_geometry_hints(self.geometry_hints)

        if "above" in metadata:
            above = metadata.boolget("above")
            if self._above != above:
                self._above = above
                self.set_keep_above(above)

        if "below" in metadata:
            below = metadata.boolget("below")
            if self._below != below:
                self._below = below
                self.set_keep_below(below)

        if "shaded" in metadata:
            shaded = metadata.boolget("shaded")
            if self._shaded != shaded:
                self._shaded = shaded
                self.set_shaded(shaded)

        if "sticky" in metadata:
            sticky = metadata.boolget("sticky")
            if self._sticky != sticky:
                self._sticky = sticky
                if sticky:
                    self.stick()
                else:
                    self.unstick()

        if "skip-taskbar" in metadata:
            skip_taskbar = metadata.boolget("skip-taskbar")
            if self._skip_taskbar != skip_taskbar:
                self._skip_taskbar = skip_taskbar
                self.set_skip_taskbar_hint(skip_taskbar)

        if "skip-pager" in metadata:
            skip_pager = metadata.boolget("skip-pager")
            if self._skip_pager != skip_pager:
                self._skip_pager = skip_pager
                self.set_skip_pager_hint(skip_pager)

        if "workspace" in metadata:
            self.set_workspace(metadata.intget("workspace"))

        if "bypass-compositor" in metadata:
            self.set_bypass_compositor(metadata.intget("bypass-compositor"))

        if "strut" in metadata:
            self.set_strut(metadata.dictget("strut", {}))

        if "fullscreen-monitors" in metadata:
            self.set_fullscreen_monitors(
                metadata.inttupleget("fullscreen-monitors"))

        if "shape" in metadata:
            self.set_shape(metadata.dictget("shape", {}))

        if "command" in metadata:
            self.set_command(metadata.strget("command"))

        if "x11-property" in metadata:
            self.set_x11_property(*metadata.tupleget("x11-property"))

        if "content-type" in metadata:
            self.content_type = metadata.strget("content-type")
Exemple #20
0
 def add_request(self, cb_answer, send_id, dtype, url, filesize, printit, openit, timeout):
     expires = monotonic()+timeout
     self.requests.append((cb_answer, net_utf8(send_id), net_utf8(dtype), net_utf8(url), filesize, printit, openit, expires))
     self.populate_table()
     if not self.populate_timer:
         self.schedule_timer()
Exemple #21
0
    def set_printers(self, printers, password_file, auth, encryption,
                     encryption_keyfile):
        log("set_printers%s for %s",
            (printers, password_file, auth, encryption, encryption_keyfile),
            self)
        if self.machine_id == get_machine_id() and not ADD_LOCAL_PRINTERS:
            self.printers = printers
            log("local client with identical machine id,")
            log(" not configuring local printers")
            return
        if not self.uuid:
            log.warn("Warning: client did not supply a UUID,")
            log.warn(" printer forwarding cannot be enabled")
            return
        #remove the printers no longer defined
        #or those whose definition has changed (and we will re-add them):
        for k in tuple(self.printers.keys()):
            cpd = self.printers.get(k)
            npd = printers.get(k)
            if cpd == npd:
                #unchanged: make sure we don't try adding it again:
                printers.pop(k, None)
                continue
            if npd is None:
                log("printer %s no longer exists", k)
            else:
                log("printer %s has been modified:", k)
                log(" was %s", cpd)
                log(" now %s", npd)
            #remove it:
            self.printers.pop(k, None)
            self.remove_printer(k)
        #expand it here so the xpraforwarder doesn't need to import anything xpra:
        attributes = {
            "display": os.environ.get("DISPLAY"),
            "source": self.uuid
        }

        def makeabs(filename):
            #convert to an absolute path since the backend may run as a different user:
            return os.path.abspath(os.path.expanduser(filename))

        if auth:
            auth_password_file = None
            try:
                name, _, authclass, authoptions = auth
                auth_password_file = authoptions.get("file")
                log("file for %s / %s: '%s'", name, authclass, password_file)
            except Exception as e:
                log.error(
                    "Error: cannot forward authentication attributes to printer backend:"
                )
                log.error(" %s", e)
            if auth_password_file or password_file:
                attributes["password-file"] = makeabs(auth_password_file
                                                      or password_file[0])
        if encryption:
            if not encryption_keyfile:
                log.error("Error: no encryption keyfile found for printing")
            else:
                attributes["encryption"] = encryption
                attributes["encryption-keyfile"] = makeabs(encryption_keyfile)
        #if we can, tell it exactly where to connect:
        if self.unix_socket_paths:
            #prefer sockets in public paths:
            attributes["socket-path"] = self.choose_socket_path()
        log("printer attributes: %s", attributes)
        for name, props in printers.items():
            printer = net_utf8(name)
            if printer not in self.printers:
                self.setup_printer(printer, props, attributes)
Exemple #22
0
 def _process_send_file(self, packet):
     #the remote end is sending us a file
     start = monotonic()
     basefilename, mimetype, printit, openit, filesize, file_data, options = packet[1:8]
     send_id = ""
     if len(packet)>=9:
         send_id = net_utf8(packet[8])
     #basefilename should be utf8:
     basefilename = net_utf8(basefilename)
     mimetype = net_utf8(mimetype)
     if filesize<=0:
         filelog.error("Error: invalid file size: %s", filesize)
         filelog.error(" file transfer aborted for %r", basefilename)
         return
     args = (send_id, "file", basefilename, printit, openit)
     r = self.accept_data(*args)
     filelog("%s%s=%s", self.accept_data, args, r)
     if r is None:
         filelog.warn("Warning: %s rejected for file '%s'",
                      ("transfer", "printing")[bool(printit)],
                      basefilename)
         return
     #accept_data can override the flags:
     printit, openit = r
     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])
     if filesize>self.file_size_limit:
         l.error("Error: file '%s' is too large:", basefilename)
         l.error(" %sB, the file size limit is %sB",
                 std_unit(filesize), std_unit(self.file_size_limit))
         return
     chunk_id = options.strget("file-chunk-id")
     try:
         filename, fd = safe_open_download_file(basefilename, mimetype)
     except OSError as e:
         filelog("cannot save file %s / %s", basefilename, mimetype, exc_info=True)
         filelog.error("Error: failed to save downloaded file")
         filelog.error(" %s", e)
         if chunk_id:
             self.send("ack-file-chunk", chunk_id, False, "failed to create file: %s" % e, 0)
         return
     self.file_descriptors.add(fd)
     if chunk_id:
         l = len(self.receive_chunks_in_progress)
         if l>=MAX_CONCURRENT_FILES:
             self.send("ack-file-chunk", chunk_id, False, "too many file transfers in progress: %i" % l, 0)
             os.close(fd)
             return
         digest = hashlib.sha256()
         chunk = 0
         timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_receiving, chunk_id, chunk)
         chunk_state = [
             monotonic(),
             fd, filename, mimetype,
             printit, openit, filesize,
             options, digest, 0, False, send_id,
             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, got %s" % (file_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="sha256", libfn=hashlib.sha256):
         digest = options.get(algo)
         if digest:
             h = libfn()
             h.update(file_data)
             l("%s digest: %s - expected: %s", algo, h.hexdigest(), digest)
             if digest!=h.hexdigest():
                 self.digest_mismatch(filename, digest, h.hexdigest(), algo)
     check_digest("sha256", hashlib.sha256)
     check_digest("sha1", hashlib.sha1)
     check_digest("md5", hashlib.md5)
     try:
         os.write(fd, file_data)
     finally:
         os.close(fd)
     self.transfer_progress_update(False, send_id, monotonic()-start, filesize, filesize, None)
     self.process_downloaded_file(filename, mimetype, printit, openit, filesize, options)
Exemple #23
0
    def _process_send_file_chunk(self, packet):
        chunk_id, chunk, file_data, has_more = packet[1:5]
        chunk_id = net_utf8(chunk_id)
        filelog("_process_send_file_chunk%s", (chunk_id, chunk, "%i bytes" % len(file_data), has_more))
        chunk_state = self.receive_chunks_in_progress.get(chunk_id)
        if not chunk_state:
            filelog.error("Error: cannot find the file transfer id '%r'", chunk_id)
            self.cancel_file(chunk_id, "file transfer id %r not found" % chunk_id, chunk)
            return
        if chunk_state[-4]:
            filelog("got chunk for a cancelled file transfer, ignoring it")
            return
        def progress(position, error=None):
            start = chunk_state[0]
            send_id = chunk_state[-3]
            filesize = chunk_state[6]
            self.transfer_progress_update(False, send_id, monotonic()-start, position, filesize, error)
        fd = chunk_state[1]
        if chunk_state[-1]+1!=chunk:
            filelog.error("Error: chunk number mismatch, expected %i but got %i", chunk_state[-1]+1, chunk)
            self.cancel_file(chunk_id, "chunk number mismatch", chunk)
            osclose(fd)
            progress(-1, "chunk no mismatch")
            return
        file_data = strtobytes(file_data)
        #update chunk number:
        chunk_state[-1] = chunk
        digest = chunk_state[8]
        written = chunk_state[9]
        try:
            os.write(fd, file_data)
            digest.update(file_data)
            written += len(file_data)
            chunk_state[9] = written
        except OSError as e:
            filelog.error("Error: cannot write file chunk")
            filelog.error(" %s", e)
            self.cancel_file(chunk_id, "write error: %s" % e, chunk)
            osclose(fd)
            progress(-1, "write error (%s)" % e)
            return
        self.send("ack-file-chunk", chunk_id, True, "", chunk)
        if has_more:
            progress(written)
            timer = chunk_state[-2]
            if timer:
                self.source_remove(timer)
            #remote end will send more after receiving the ack
            timer = self.timeout_add(CHUNK_TIMEOUT, self._check_chunk_receiving, chunk_id, chunk)
            chunk_state[-2] = timer
            return
        self.receive_chunks_in_progress.pop(chunk_id, None)
        osclose(fd)
        #check file size and digest then process it:
        filename, mimetype, printit, openit, filesize, options = chunk_state[2:8]
        if written!=filesize:
            filelog.error("Error: expected a file of %i bytes, got %i", filesize, written)
            progress(-1, "file size mismatch")
            return
        expected_digest = options.strget("sha1")
        if expected_digest and digest.hexdigest()!=expected_digest:
            progress(-1, "checksum mismatch")
            self.digest_mismatch(filename, digest, expected_digest, "sha1")
            return

        progress(written)
        start_time = chunk_state[0]
        elapsed = monotonic()-start_time
        mimetype = bytestostr(mimetype)
        filelog("%i bytes received in %i chunks, took %ims", filesize, chunk, elapsed*1000)
        self.process_downloaded_file(filename, mimetype, printit, openit, filesize, options)