xpra icon
Bug tracker and wiki

Ticket #1026: file-transfer-refactoring.diff

File file-transfer-refactoring.diff, 29.6 KB (added by Antoine Martin, 3 years ago)

refactoring needed to add chunking code only in one place

  • xpra/client/client_base.py

     
    135135        self.speed = opts.speed
    136136        self.min_speed = opts.min_speed
    137137        #printing and file transfer:
    138         FileTransferHandler.init(self, opts)
     138        FileTransferHandler.init_opts(self, opts)
    139139
    140140        if DETECT_LEAKS:
    141141            from xpra.util import detect_leaks
     
    643643            return False
    644644        self.parse_printing_capabilities()
    645645        self.parse_logging_capabilities()
     646        self.parse_file_transfer_caps(self.server_capabilities)
    646647        netlog("server_connection_established() adding authenticated packet handlers")
    647648        self.init_authenticated_packet_handlers()
    648649        return True
  • xpra/client/gtk_base/gtk_client_base.py

     
    150150        return self.start_new_command
    151151
    152152    def show_file_upload(self, *args):
    153         filelog("show_file_upload%s can open=%s", args, self.server_open_files)
     153        filelog("show_file_upload%s can open=%s", args, self.remote_open_files)
    154154        buttons = [gtk.STOCK_CANCEL,    gtk.RESPONSE_CANCEL]
    155         if self.server_open_files:
     155        if self.remote_open_files:
    156156            buttons += [gtk.STOCK_OPEN,      gtk.RESPONSE_ACCEPT]
    157157        buttons += [gtk.STOCK_OK,        gtk.RESPONSE_OK]
    158158        dialog = gtk.FileChooserDialog("File to upload", parent=None, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=tuple(buttons))
     
    167167        data, filesize, entity = gfile.load_contents()
    168168        filelog("load_contents: filename=%s, %i bytes, entity=%s, response=%s", filename, filesize, entity, v)
    169169        dialog.destroy()
    170         self.send_file(filename, data, filesize, openit=(v==gtk.RESPONSE_ACCEPT))
     170        self.send_file(filename, "", data, filesize=filesize, openit=(v==gtk.RESPONSE_ACCEPT))
    171171
    172172
    173173    def show_about(self, *args):
  • xpra/client/gtk_base/gtk_tray_menu_base.py

     
    10911091    def make_uploadmenuitem(self):
    10921092        self.upload = self.menuitem("Upload File", "upload.png", "Send a file to the server", self.client.show_file_upload)
    10931093        def enable_upload(*args):
    1094             log("enable_upload%s server_file_transfer=%s", args, self.client.server_file_transfer)
    1095             set_sensitive(self.upload, self.client.server_file_transfer)
    1096             if not self.client.server_file_transfer:
     1094            log("enable_upload%s server_file_transfer=%s", args, self.client.remote_file_transfer)
     1095            set_sensitive(self.upload, self.client.remote_file_transfer)
     1096            if not self.client.remote_file_transfer:
    10971097                self.upload.set_tooltip_text("Not supported by the server")
    10981098        self.client.after_handshake(enable_upload)
    10991099        return self.upload
  • xpra/client/ui_client_base.py

     
    1212import datetime
    1313import traceback
    1414import logging
    15 import hashlib
    1615from collections import deque
    1716from threading import RLock
    1817
     
    254253        self.server_is_shadow = False
    255254        self.server_supports_sharing = False
    256255        self.server_supports_window_filters = False
    257         self.server_file_transfer = False
    258         self.server_file_size_limit = 10
    259         self.server_open_files = False
    260256        #what we told the server about our encoding defaults:
    261257        self.encoding_defaults = {}
    262258
     
    11901186        log("send_start_command(%s, %s, %s, %s)", name, command, ignore, sharing)
    11911187        self.send("start-command", name, command, ignore, sharing)
    11921188
    1193     def send_file(self, filename, data, filesize, openit):
    1194         if not self.file_transfer:
    1195             filelog.warn("Warning: file transfers are not enabled for %s", self)
    1196             return False
    1197         assert len(data)>=filesize
    1198         data = data[:filesize]          #gio may null terminate it
    1199         filelog("send_file%s", (filename, "%i bytes" % filesize, openit))
    1200         absfile = os.path.abspath(filename)
    1201         basefilename = os.path.basename(filename)
    1202         cdata = self.compressed_wrapper("file-data", data)
    1203         assert len(cdata)<=filesize     #compressed wrapper ensures this is true
    1204         if filesize>self.file_size_limit*1024*1024:
    1205             filelog.warn("Warning: cannot upload the file '%s'", basefilename)
    1206             filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
    1207             filelog.warn(" the file size limit is %iMB", self.file_size_limit)
    1208             return False
    1209         if filesize>self.server_file_size_limit*1024*1024:
    1210             filelog.warn("Warning: cannot upload the file '%s'", basefilename)
    1211             filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
    1212             filelog.warn(" the file size limit for %s is %iMB", self._protocol, self.server_file_size_limit)
    1213             return False
    1214         printit = False
    1215         mimetype = ""
    1216         u = hashlib.sha1()
    1217         u.update(data)
    1218         filelog("sha1 digest(%s)=%s", absfile, u.hexdigest())
    1219         options = {"sha1"       : u.hexdigest()}
    1220         self.send("send-file", basefilename, mimetype, printit, openit, filesize, cdata, options)
    1221         return True
    12221189
    1223 
    12241190    def send_focus(self, wid):
    12251191        focuslog("send_focus(%s)", wid)
    12261192        self.send("focus", wid, self.get_current_modifiers())
     
    17751741            default_rpc_types = []
    17761742        self.server_rpc_types = c.strlistget("rpc-types", default_rpc_types)
    17771743        self.start_new_commands = c.boolget("start-new-commands")
    1778         self.server_file_transfer = c.boolget("file-transfer")
    1779         self.server_file_size_limit = c.intget("file-size-limit", 10)
    1780         self.server_open_files = c.boolget("open-files")
    17811744        self.mmap_enabled = self.supports_mmap and self.mmap_enabled and c.boolget("mmap_enabled")
    17821745        if self.mmap_enabled:
    17831746            mmap_token = c.intget("mmap_token")
  • xpra/net/file_transfer.py

     
    11# This file is part of Xpra.
    2 # Copyright (C) 2010-2015 Antoine Martin <antoine@devloop.org.uk>
     2# Copyright (C) 2010-2016 Antoine Martin <antoine@devloop.org.uk>
    33# Copyright (C) 2008, 2010 Nathaniel Smith <njs@pobox.com>
    44# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
    55# later version. See the file COPYING for details.
    66
    77import os
     8import time
     9import subprocess, shlex
     10import hashlib
    811
    912from xpra.log import Logger
    1013printlog = Logger("printing")
     
    1215
    1316from xpra.child_reaper import getChildReaper
    1417from xpra.util import typedict, csv
     18from xpra.simple_stats import std_unit
    1519
    1620DELETE_PRINTER_FILE = os.environ.get("XPRA_DELETE_PRINTER_FILE", "1")=="1"
    1721
    1822
    19 class FileTransferHandler(object):
    20     """ Utility class for receiving files and optionally printing them,
    21         used by both clients and server to share the common code and attributes
    22     """
     23class FileTransferAttributes(object):
     24    def __init__(self, opts=None):
     25        if opts:
     26            self.init_opts(opts)
     27        else:
     28            self.init_attributes()
    2329
    24     def __init__(self):
    25         self.file_transfer = False
    26         self.file_size_limit = 10
    27         self.printing = False
    28         self.open_files = False
    29         self.open_command = None
     30    def init_opts(self, opts):
     31        #get the settings from a config object
     32        self.init_attributes(opts.file_transfer, opts.file_size_limit, opts.printing, opts.open_files, opts.open_command)
    3033
    31     def init(self, opts):
     34    def init_attributes(self, file_transfer=False, file_size_limit=10, printing=False, open_files=False, open_command=None):
    3235        #printing and file transfer:
    33         self.file_transfer = opts.file_transfer
    34         self.file_size_limit = opts.file_size_limit
    35         self.printing = opts.printing
    36         self.open_command = opts.open_command
    37         self.open_files = opts.open_files
     36        self.file_transfer = file_transfer
     37        self.file_size_limit = file_size_limit
     38        self.printing = printing
     39        self.open_files = open_files
     40        self.open_command = open_command
    3841
    39 
    4042    def get_file_transfer_features(self):
     43        #used in hello packets
    4144        return {
    42                  "file-transfer"                : self.file_transfer,
    43                  "file-size-limit"              : self.file_size_limit,
    44                  "open-files"                   : self.open_files,
    45                  "printing"                     : self.printing,
    46                  }
     45                "file-transfer"     : self.file_transfer,
     46                "file-size-limit"   : self.file_size_limit,
     47                "open-files"        : self.open_files,
     48                "printing"          : self.printing,
     49                }
    4750
    48     def get_file_transfer_info(self):
     51    def get_info(self):
    4952        #slightly different from above... for legacy reasons
    50         #this one is used in a proper "file." namespace from server_base.py
     53        #this one is used for get_info() in a proper "file." namespace from server_base.py
    5154        return {
    52                  "transfer"                     : self.file_transfer,
    53                  "size-limit"                   : self.file_size_limit,
    54                  "open"                         : self.open_files,
    55                  }
     55                "enabled"           : self.file_transfer,
     56                "size-limit"        : self.file_size_limit,
     57                "open"              : self.open_files,
     58                }
    5659
    5760
     61class FileTransferHandler(FileTransferAttributes):
     62    """
     63        Utility class for receiving files and optionally printing them,
     64        used by both clients and server to share the common code and attributes
     65    """
     66
     67    def init_attributes(self, *args):
     68        FileTransferAttributes.init_attributes(self, *args)
     69        self.remote_file_transfer = False
     70        self.remote_printing = False
     71        self.remote_open_files = False
     72        self.remote_file_size_limit = 0
     73
     74    def parse_file_transfer_caps(self, c):
     75        self.remote_file_transfer = c.boolget("file-transfer")
     76        self.remote_printing = c.boolget("printing")
     77        self.remote_open_files = c.boolget("open-files")
     78        self.remote_file_size_limit = c.boolget("file-size-limit")
     79
     80    def get_info(self):
     81        info = FileTransferAttributes.get_info(self)
     82        info["remote"] = {
     83                          "file-transfer"   : self.remote_file_transfer,
     84                          "printing"        : self.remote_printing,
     85                          "open-files"      : self.remote_open_files,
     86                          "file-size-limit" : self.remote_file_size_limit,
     87                          }
     88        return info
     89
     90
    5891    def _process_send_file(self, packet):
    5992        #send-file basefilename, printit, openit, filesize, 0, data)
    6093        from xpra.platform.paths import get_download_dir
     
    78111            l.error(" %iMB, the file size limit is %iMB", filesize//1024//1024, self.file_size_limit)
    79112            return
    80113        #check digest if present:
    81         import hashlib
    82114        def check_digest(algo="sha1", libfn=hashlib.sha1):
    83115            digest = options.get(algo)
    84116            if not digest:
     
    125157        finally:
    126158            os.close(fd)
    127159        l.info("downloaded %s bytes to %s file%s:", filesize, (mimetype or "unknown"), ["", " for printing"][int(printit)])
    128         l.info(" %s", filename)
     160        l.info(" '%s'", filename)
    129161        if printit:
    130162            printer = options.strget("printer")
    131163            title   = options.strget("title")
     
    141173            self._open_file(filename)
    142174
    143175    def _print_file(self, filename, mimetype, printer, title, options):
    144         import time
    145176        from xpra.platform.printing import print_files, printing_finished, get_printers
    146177        printers = get_printers()
    147178        if printer not in printers:
     
    183214            filelog.warn(" ignoring uploaded file:")
    184215            filelog.warn(" '%s'", filename)
    185216            return
    186         import subprocess, shlex
    187217        command = shlex.split(self.open_command)+[filename]
    188218        proc = subprocess.Popen(command)
    189219        cr = getChildReaper()
     
    194224                filelog.warn("Warning: failed to open the downloaded file")
    195225                filelog.warn(" '%s %s' returned %s", self.open_command, filename, returncode)
    196226        cr.add_process(proc, "Open File %s" % filename, command, True, True, open_done)
     227
     228    def send_file(self, filename, mimetype, data, filesize=0, printit=False, openit=False, options={}):
     229        if printit:
     230            if not self.printing:
     231                printlog.warn("Warning: printing is not enabled for %s", self)
     232                return False
     233            if not self.remote_printing:
     234                printlog.warn("Warning: remote end does not support printing")
     235                return False
     236            action = "print"
     237            l = printlog
     238        else:
     239            if not self.file_transfer:
     240                filelog.warn("Warning: file transfers are not enabled for %s", self)
     241                return False
     242            if not self.remote_file_transfer:
     243                printlog.warn("Warning: remote end does not support file transfers")
     244                return False
     245            action = "upload"
     246            l = filelog
     247        l("send_file%s", (filename, mimetype, "%s bytes" % len(data), printit, openit, options))
     248        if not printit and openit and not self.remote_open_files:
     249            l.warn("Warning: opening the file after transfer is disabled on the remote end")
     250            openit = False
     251        assert len(data)>=filesize, "data is smaller then the given file size!"
     252        data = data[:filesize]          #gio may null terminate it
     253        filelog("send_file%s", (filename, "%i bytes" % filesize, openit))
     254        absfile = os.path.abspath(filename)
     255        basefilename = os.path.basename(filename)
     256        cdata = self.compressed_wrapper("file-data", data)
     257        assert len(cdata)<=filesize     #compressed wrapper ensures this is true
     258        if filesize>self.file_size_limit*1024*1024:
     259            filelog.warn("Warning: cannot %s the file '%s'", action, basefilename)
     260            filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
     261            filelog.warn(" the file size limit is %iMB", self.file_size_limit)
     262            return False
     263        if filesize>self.remote_file_size_limit*1024*1024:
     264            filelog.warn("Warning: cannot %s the file '%s'", action, basefilename)
     265            filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
     266            filelog.warn(" the remote file size limit is %iMB", self.remote_file_size_limit)
     267            return False
     268        mimetype = ""
     269        u = hashlib.sha1()
     270        u.update(data)
     271        filelog("sha1 digest(%s)=%s", absfile, u.hexdigest())
     272        options = {"sha1" : u.hexdigest()}
     273        self.send("send-file", basefilename, mimetype, printit, openit, filesize, cdata, options)
     274        return True
  • xpra/server/server_base.py

     
    2121clientlog = Logger("client")
    2222screenlog = Logger("screen")
    2323printlog = Logger("printing")
     24filelog = Logger("file")
    2425netlog = Logger("network")
    2526metalog = Logger("metadata")
    2627windowlog = Logger("window")
     
    4546from xpra.scripts.main import sound_option
    4647from xpra.codecs.loader import PREFERED_ENCODING_ORDER, PROBLEMATIC_ENCODINGS, load_codecs, codec_versions, has_codec, get_codec
    4748from xpra.codecs.video_helper import getVideoHelper, ALL_VIDEO_ENCODER_OPTIONS, ALL_CSC_MODULE_OPTIONS
    48 from xpra.net.file_transfer import FileTransferHandler
     49from xpra.net.file_transfer import FileTransferAttributes
    4950if sys.version > '3':
    5051    unicode = str           #@ReservedAssignment
    5152
     
    7677    return d
    7778
    7879
    79 class ServerBase(ServerCore, FileTransferHandler):
     80class ServerBase(ServerCore):
    8081    """
    8182        This is the base class for servers.
    8283        It provides all the generic functions but is not tied
     
    8687
    8788    def __init__(self):
    8889        ServerCore.__init__(self)
    89         FileTransferHandler.__init__(self)
    9090        log("ServerBase.__init__()")
    9191        self.init_uuid()
    9292
     
    132132        self.dbus_server = None
    133133        self.lpadmin = ""
    134134        self.lpinfo = ""
     135        self.file_transfer = FileTransferAttributes()
    135136        #starting child commands:
    136137        self.child_display = None
    137138        self.start_commands = []
     
    242243        self.env = parse_env(opts.env)
    243244        self.send_pings = opts.pings
    244245        #printing and file transfer:
    245         FileTransferHandler.init(self, opts)
     246        self.file_transfer.init_opts(opts)
    246247        self.lpadmin = opts.lpadmin
    247248        self.lpinfo = opts.lpinfo
    248249        self.av_sync = opts.av_sync
     
    10541055                          self.get_transient_for, self.get_focus, self.get_cursor_data,
    10551056                          get_window_id,
    10561057                          self.window_filters,
    1057                           self.printing,
     1058                          self.printing, self.file_transfer,
    10581059                          self.supports_mmap, self.av_sync,
    10591060                          self.core_encodings, self.encodings, self.default_encoding, self.scaling_control,
    10601061                          self.sound_properties,
     
    10661067        log("process_hello serversource=%s", ss)
    10671068        try:
    10681069            ss.parse_hello(c, self.min_mmap_size)
    1069             proto.max_packet_size = max(proto.max_packet_size, int(ss.file_transfer) * (1024 + ss.file_size_limit*1024*1024))
    10701070        except:
    10711071            #close it already
    10721072            ss.close()
     
    10761076        send_ui = ui_client and not is_request
    10771077        self.idle_add(self.parse_hello_ui, ss, c, auth_caps, send_ui, share_count)
    10781078
    1079     def accept_client(self, proto, c):
    1080         ServerCore.accept_client(self, proto, c)
    1081         #may need to bump file size limit for file transfers:
    1082         proto.max_packet_size = max(proto.max_packet_size, int(self.file_transfer) * (1024 + self.file_size_limit*1024*1024))
    10831079
    1084 
    10851080    def get_server_source_class(self):
    10861081        from xpra.server.source import ServerSource
    10871082        return ServerSource
     
    12951290                 "webcam.encodings"             : self.webcam_encodings,
    12961291                 "virtual-video-devices"        : self.virtual_video_devices,
    12971292                 })
    1298             capabilities.update(self.get_file_transfer_features())
     1293            capabilities.update(self.file_transfer.get_file_transfer_features())
    12991294            capabilities.update(flatten_dict(self.get_server_features()))
    13001295        #this is a feature, but we would need the hello request
    13011296        #to know if it is really needed.. so always include it:
     
    14351430        return sources
    14361431
    14371432    def control_command_send_file(self, filename, openit, client_uuids, maxbitrate=0):
    1438         actual_filename = os.path.abspath(os.path.expanduser(filename))
    1439         if not os.path.exists(actual_filename):
    1440             raise ControlError("file '%s' does not exist" % filename)
    14411433        openit = str(openit).lower() in ("open", "true", "1")
    1442         #find the client uuid specified:
     1434        return self.do_control_file_command("send file", client_uuids, filename, "file_tranfer", (False, openit))
     1435
     1436    def control_command_print(self, filename, printer, client_uuids, maxbitrate=0, title="", *options_strs):
     1437        #parse options into a dict:
     1438        options = {}
     1439        for arg in options_strs:
     1440            argp = arg.split("=", 1)
     1441            if len(argp)==2 and len(argp[0])>0:
     1442                options[argp[0]] = argp[1]
     1443        return self.do_control_file_command("print", client_uuids, filename, "printing", (True, True, options))
     1444
     1445    def do_control_file_command(self, command_type, client_uuids, filename, source_flag_name, send_file_args):
     1446        #find the clients:
    14431447        sources = self._control_get_sources(client_uuids)
    14441448        if not sources:
    14451449            raise ControlError("no clients found matching: %s" % client_uuids)
    1446         data = load_binary_file(actual_filename)
    1447         file_size_MB = len(data)//1024//1024
    1448         if file_size_MB>self.file_size_limit:
    1449             raise ControlError("file '%s' is too large: %iMB (limit is %iMB)" % (filename, file_size_MB, self.file_size_limit))
    1450         for ss in sources:
    1451             if ss.file_transfer:
    1452                 ss.send_file(filename, "", data, False, openit)
    1453             else:
    1454                 log.warn("cannot send file, client %s does not support file transfers!", ss)
    1455         return "file transfer of '%s' to %s initiated" % (filename, client_uuids)
    1456 
    1457     def control_command_print(self, filename, printer, client_uuids, maxbitrate=0, title="", *options_strs):
     1450        #find the file and load it:
    14581451        actual_filename = os.path.abspath(os.path.expanduser(filename))
    14591452        try:
    14601453            stat = os.stat(actual_filename)
    1461             printlog("os.stat(%s)=%s", actual_filename, stat)
     1454            filelog("os.stat(%s)=%s", actual_filename, stat)
    14621455        except os.error:
    1463             printlog("os.stat(%s)", actual_filename, exc_info=True)
     1456            filelog("os.stat(%s)", actual_filename, exc_info=True)
    14641457        if not os.path.exists(actual_filename):
    14651458            raise ControlError("file '%s' does not exist" % filename)
    1466         sources = self._control_get_sources(client_uuids)
    1467         if not sources:
    1468             raise ControlError("no clients found matching: %s" % client_uuids)
    1469         #parse options into a dict:
    1470         options = {}
    1471         for arg in options_strs:
    1472             argp = arg.split("=", 1)
    1473             if len(argp)==2 and len(argp[0])>0:
    1474                 options[argp[0]] = argp[1]
    14751459        data = load_binary_file(actual_filename)
     1460        #verify size:
    14761461        file_size_MB = len(data)//1024//1024
    14771462        if file_size_MB>self.file_size_limit:
    14781463            raise ControlError("file '%s' is too large: %iMB (limit is %iMB)" % (filename, file_size_MB, self.file_size_limit))
     1464        return sources, data
     1465        #send it to each client:
    14791466        for ss in sources:
    1480             if ss.printing:
    1481                 ss.send_file(filename, "", data, True, True, options)
     1467            if not getattr(ss, source_flag_name):       #ie: ServerSource.file_transfer
     1468                log.warn("Warning: cannot %s '%s'", command_type, filename)
     1469                log.warn(" client %s does not support this feature", ss)
     1470            elif file_size_MB>ss.file_size_limit:
     1471                log.warn("Warning: cannot %s '%s'", command_type, filename)
     1472                log.warn(" client %s file size limit is %iMB (file is %iMB)", ss, ss.file_size_limit, file_size_MB)
    14821473            else:
    1483                 printlog.warn("client %s does not support printing!", ss)
    1484         return "printing to %s initiated" % client_uuids
     1474                ss.send_file(filename, "", data, *send_file_args)
     1475        return "%s of '%s' to %s initiated" % (command_type, client_uuids)
    14851476
     1477
    14861478    def control_command_compression(self, compression):
    14871479        c = compression.lower()
    14881480        from xpra.net import compression
     
    17071699
    17081700
    17091701    def _process_send_file(self, proto, packet):
    1710         #superclass does not take the protocol as argument:
    1711         FileTransferHandler._process_send_file(self, packet)
     1702        ss = self._server_sources.get(proto)
     1703        if not ss:
     1704            log.warn("Warning: invalid client source for file packet")
     1705            return
     1706        ss._process_send_file(packet)
    17121707
    17131708    def _process_print(self, proto, packet):
    17141709        #ie: from the xpraforwarder we call this command:
     
    18791874             "rpc-types"        : self.rpc_handlers.keys(),
    18801875             "clipboard"        : self.supports_clipboard,
    18811876             "idle_timeout"     : self.idle_timeout,
    1882              "file-size-limit"  : self.file_size_limit,
    18831877             }
    18841878        i.update(self.get_server_features())
    18851879        return i
     
    19271921        def up(prefix, d):
    19281922            info[prefix] = d
    19291923
     1924        up("file",      self.file_transfer.get_info())
    19301925        up("webcam",    self.get_webcam_info())
    1931         up("file",      self.get_file_transfer_info())
    19321926        up("printing",  self.get_printing_info())
    19331927        up("commands",  self.get_commands_info())
    19341928        up("features",  self.get_features_info())
  • xpra/server/source.py

     
    3838from xpra.codecs.codec_constants import video_spec
    3939from xpra.net import compression
    4040from xpra.net.compression import compressed_wrapper, Compressed, Uncompressed
     41from xpra.net.file_transfer import FileTransferHandler
    4142from xpra.make_thread import make_thread
    4243from xpra.os_util import platform_name, Queue, get_machine_id, get_user_uuid, BytesIOClass
    4344from xpra.server.background_worker import add_work_item
     
    200201        return not(WindowPropertyIn.evaluate(window_value))
    201202
    202203
    203 class ServerSource(object):
     204class ServerSource(FileTransferHandler):
    204205    """
    205206    A ServerSource represents a client connection.
    206207    It mediates between the server class (which only knows about actual window objects and display server events)
     
    226227                 get_transient_for, get_focus, get_cursor_data_cb,
    227228                 get_window_id,
    228229                 window_filters,
    229                  printing,
     230                 printing, file_transfer,
    230231                 supports_mmap, av_sync,
    231232                 core_encodings, encodings, default_encoding, scaling_control,
    232233                 sound_properties,
     
    241242                 get_transient_for, get_focus,
    242243                 get_window_id,
    243244                 window_filters,
    244                  printing,
     245                 printing, file_transfer,
    245246                 supports_mmap, av_sync,
    246247                 core_encodings, encodings, default_encoding, scaling_control,
    247248                 sound_properties,
     
    250251                 speaker_codecs, microphone_codecs,
    251252                 default_quality, default_min_quality,
    252253                 default_speed, default_min_speed))
     254        FileTransferHandler.__init__(self, file_transfer)
    253255        global counter
    254256        self.counter = counter.increase()
    255257        self.close_event = Event()
     
    416418        self.metadata_supported = []
    417419        self.show_desktop_allowed = False
    418420        self.supports_transparency = False
    419         self.file_transfer = False
    420         self.file_size_limit = 10
    421421        self.printers = {}
    422422        self.vrefresh = -1
    423423        self.double_click_time  = -1
     
    680680        self.client_revision = c.strget("build.revision")
    681681        self.client_proxy = c.boolget("proxy")
    682682        self.client_wm_name = c.strget("wm_name")
     683        #file transfers and printing:
     684        self.parse_file_transfer_caps(c)
    683685        #general features:
    684686        self.zlib = c.boolget("zlib", True)
    685687        self.lz4 = c.boolget("lz4", False) and compression.use_lz4
     
    698700        self.clipboard_notifications = c.boolget("clipboard.notifications")
    699701        self.clipboard_set_enabled = c.boolget("clipboard.set_enabled")
    700702        self.share = c.boolget("share")
    701         self.file_transfer = c.boolget("file-transfer")
    702         self.file_size_limit = c.intget("file-size-limit")
    703         self.printing = self.printing and c.boolget("printing")
    704703        self.window_initiate_moveresize = c.boolget("window.initiate-moveresize")
    705704        self.system_tray = c.boolget("system_tray")
    706705        self.control_commands = c.strlistget("control_commands")
     
    794793                self.add_window_filter(object_name, property_name, operator, value)
    795794        except Exception as e:
    796795            log.error("Error parsing window-filters: %s", e)
     796        #adjust max packet size if file transfers are enabled:
     797        if self.file_transfer:
     798            self.protocol.max_packet_size = max(self.protocol.max_packet_size, self.file_size_limit*1024*1024)
    797799
    798800
    799801    def parse_encoding_caps(self, c, min_mmap_size):
     
    14641466                    finfo[i] = str(f)
    14651467                    i += 1
    14661468            info["window-filter"] = finfo
    1467         info.update(self.get_sound_info())
     1469        info["file-transfers"] = FileTransferHandler.get_info(self)
     1470        info["sound"] = self.get_sound_info()
    14681471        info.update(self.get_features_info())
    14691472        info.update(self.get_screen_info())
    14701473        return info
  • xpra/platform/darwin/osx_menu.py

     
    223223            def add_ah(*args):
    224224                if self.client.start_new_commands:
    225225                    actions_menu.add(self.make_startnewcommandmenuitem())
    226                 if self.client.server_file_transfer:
     226                if self.client.remote_file_transfer:
    227227                    actions_menu.add(self.make_uploadmenuitem())
    228228            self.client.after_handshake(add_ah)
    229229            menus.append(("Actions", actions_menu))