Skip to content

Commit

Permalink
#1026: refactor more of the file transfer code into the utility module
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@12810 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jun 13, 2016
1 parent 7d2f990 commit 1352ed9
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 195 deletions.
6 changes: 5 additions & 1 deletion src/xpra/client/client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def init(self, opts):
self.speed = opts.speed
self.min_speed = opts.min_speed
#printing and file transfer:
FileTransferHandler.init(self, opts)
FileTransferHandler.init_opts(self, opts)

if DETECT_LEAKS:
from xpra.util import detect_leaks
Expand Down Expand Up @@ -643,6 +643,10 @@ def server_connection_established(self):
return False
self.parse_printing_capabilities()
self.parse_logging_capabilities()
self.parse_file_transfer_caps(self.server_capabilities)
#raise packet size if required:
if self.file_transfer:
self._protocol.max_packet_size = max(self._protocol.max_packet_size, self.file_size_limit*1024*1024)
netlog("server_connection_established() adding authenticated packet handlers")
self.init_authenticated_packet_handlers()
return True
Expand Down
6 changes: 3 additions & 3 deletions src/xpra/client/gtk_base/gtk_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ def run_command_cb(command, sharing=True):
return self.start_new_command

def show_file_upload(self, *args):
filelog("show_file_upload%s can open=%s", args, self.server_open_files)
filelog("show_file_upload%s can open=%s", args, self.remote_open_files)
buttons = [gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL]
if self.server_open_files:
if self.remote_open_files:
buttons += [gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT]
buttons += [gtk.STOCK_OK, gtk.RESPONSE_OK]
dialog = gtk.FileChooserDialog("File to upload", parent=None, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=tuple(buttons))
Expand All @@ -167,7 +167,7 @@ def show_file_upload(self, *args):
data, filesize, entity = gfile.load_contents()
filelog("load_contents: filename=%s, %i bytes, entity=%s, response=%s", filename, filesize, entity, v)
dialog.destroy()
self.send_file(filename, data, filesize, openit=(v==gtk.RESPONSE_ACCEPT))
self.send_file(filename, "", data, filesize=filesize, openit=(v==gtk.RESPONSE_ACCEPT))


def show_about(self, *args):
Expand Down
6 changes: 3 additions & 3 deletions src/xpra/client/gtk_base/gtk_tray_menu_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,9 +1091,9 @@ def enable_start_new_command(*args):
def make_uploadmenuitem(self):
self.upload = self.menuitem("Upload File", "upload.png", "Send a file to the server", self.client.show_file_upload)
def enable_upload(*args):
log("enable_upload%s server_file_transfer=%s", args, self.client.server_file_transfer)
set_sensitive(self.upload, self.client.server_file_transfer)
if not self.client.server_file_transfer:
log("enable_upload%s server_file_transfer=%s", args, self.client.remote_file_transfer)
set_sensitive(self.upload, self.client.remote_file_transfer)
if not self.client.remote_file_transfer:
self.upload.set_tooltip_text("Not supported by the server")
self.client.after_handshake(enable_upload)
return self.upload
Expand Down
37 changes: 0 additions & 37 deletions src/xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import datetime
import traceback
import logging
import hashlib
from collections import deque
from threading import RLock

Expand Down Expand Up @@ -254,9 +253,6 @@ def __init__(self):
self.server_is_shadow = False
self.server_supports_sharing = False
self.server_supports_window_filters = False
self.server_file_transfer = False
self.server_file_size_limit = 10
self.server_open_files = False
#what we told the server about our encoding defaults:
self.encoding_defaults = {}

Expand Down Expand Up @@ -1190,36 +1186,6 @@ def send_start_command(self, name, command, ignore, sharing=True):
log("send_start_command(%s, %s, %s, %s)", name, command, ignore, sharing)
self.send("start-command", name, command, ignore, sharing)

def send_file(self, filename, data, filesize, openit):
if not self.file_transfer:
filelog.warn("Warning: file transfers are not enabled for %s", self)
return False
assert len(data)>=filesize
data = data[:filesize] #gio may null terminate it
filelog("send_file%s", (filename, "%i bytes" % filesize, openit))
absfile = os.path.abspath(filename)
basefilename = os.path.basename(filename)
cdata = self.compressed_wrapper("file-data", data)
assert len(cdata)<=filesize #compressed wrapper ensures this is true
if filesize>self.file_size_limit*1024*1024:
filelog.warn("Warning: cannot upload the file '%s'", basefilename)
filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
filelog.warn(" the file size limit is %iMB", self.file_size_limit)
return False
if filesize>self.server_file_size_limit*1024*1024:
filelog.warn("Warning: cannot upload the file '%s'", basefilename)
filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
filelog.warn(" the file size limit for %s is %iMB", self._protocol, self.server_file_size_limit)
return False
printit = False
mimetype = ""
u = hashlib.sha1()
u.update(data)
filelog("sha1 digest(%s)=%s", absfile, u.hexdigest())
options = {"sha1" : u.hexdigest()}
self.send("send-file", basefilename, mimetype, printit, openit, filesize, cdata, options)
return True


def send_focus(self, wid):
focuslog("send_focus(%s)", wid)
Expand Down Expand Up @@ -1775,9 +1741,6 @@ def parse_server_capabilities(self):
default_rpc_types = []
self.server_rpc_types = c.strlistget("rpc-types", default_rpc_types)
self.start_new_commands = c.boolget("start-new-commands")
self.server_file_transfer = c.boolget("file-transfer")
self.server_file_size_limit = c.intget("file-size-limit", 10)
self.server_open_files = c.boolget("open-files")
self.mmap_enabled = self.supports_mmap and self.mmap_enabled and c.boolget("mmap_enabled")
if self.mmap_enabled:
mmap_token = c.intget("mmap_token")
Expand Down
164 changes: 126 additions & 38 deletions src/xpra/net/file_transfer.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,91 @@
# This file is part of Xpra.
# Copyright (C) 2010-2015 Antoine Martin <[email protected]>
# Copyright (C) 2010-2016 Antoine Martin <[email protected]>
# Copyright (C) 2008, 2010 Nathaniel Smith <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import time
import subprocess, shlex
import hashlib

from xpra.log import Logger
printlog = Logger("printing")
filelog = Logger("file")

from xpra.child_reaper import getChildReaper
from xpra.util import typedict, csv
from xpra.simple_stats import std_unit

DELETE_PRINTER_FILE = os.environ.get("XPRA_DELETE_PRINTER_FILE", "1")=="1"


class FileTransferHandler(object):
""" Utility class for receiving files and optionally printing them,
used by both clients and server to share the common code and attributes
"""
class FileTransferAttributes(object):
def __init__(self, opts=None):
if opts:
self.init_opts(opts)
else:
self.init_attributes()

def __init__(self):
self.file_transfer = False
self.file_size_limit = 10
self.printing = False
self.open_files = False
self.open_command = None
def init_opts(self, opts):
#get the settings from a config object
self.init_attributes(opts.file_transfer, opts.file_size_limit, opts.printing, opts.open_files, opts.open_command)

def init(self, opts):
def init_attributes(self, file_transfer=False, file_size_limit=10, printing=False, open_files=False, open_command=None):
#printing and file transfer:
self.file_transfer = opts.file_transfer
self.file_size_limit = opts.file_size_limit
self.printing = opts.printing
self.open_command = opts.open_command
self.open_files = opts.open_files

self.file_transfer = file_transfer
self.file_size_limit = file_size_limit
self.printing = printing
self.open_files = open_files
self.open_command = open_command

def get_file_transfer_features(self):
#used in hello packets
return {
"file-transfer" : self.file_transfer,
"file-size-limit" : self.file_size_limit,
"open-files" : self.open_files,
"printing" : self.printing,
}
"file-transfer" : self.file_transfer,
"file-size-limit" : self.file_size_limit,
"open-files" : self.open_files,
"printing" : self.printing,
}

def get_file_transfer_info(self):
def get_info(self):
#slightly different from above... for legacy reasons
#this one is used in a proper "file." namespace from server_base.py
#this one is used for get_info() in a proper "file." namespace from server_base.py
return {
"transfer" : self.file_transfer,
"size-limit" : self.file_size_limit,
"open" : self.open_files,
}
"enabled" : self.file_transfer,
"size-limit" : self.file_size_limit,
"open" : self.open_files,
}


class FileTransferHandler(FileTransferAttributes):
"""
Utility class for receiving files and optionally printing them,
used by both clients and server to share the common code and attributes
"""

def init_attributes(self, *args):
FileTransferAttributes.init_attributes(self, *args)
self.remote_file_transfer = False
self.remote_printing = False
self.remote_open_files = False
self.remote_file_size_limit = 0

def parse_file_transfer_caps(self, c):
self.remote_file_transfer = c.boolget("file-transfer")
self.remote_printing = c.boolget("printing")
self.remote_open_files = c.boolget("open-files")
self.remote_file_size_limit = c.boolget("file-size-limit")

def get_info(self):
info = FileTransferAttributes.get_info(self)
info["remote"] = {
"file-transfer" : self.remote_file_transfer,
"printing" : self.remote_printing,
"open-files" : self.remote_open_files,
"file-size-limit" : self.remote_file_size_limit,
}
return info


def _process_send_file(self, packet):
Expand All @@ -78,7 +111,6 @@ def _process_send_file(self, packet):
l.error(" %iMB, the file size limit is %iMB", filesize//1024//1024, self.file_size_limit)
return
#check digest if present:
import hashlib
def check_digest(algo="sha1", libfn=hashlib.sha1):
digest = options.get(algo)
if not digest:
Expand Down Expand Up @@ -124,11 +156,15 @@ def check_digest(algo="sha1", libfn=hashlib.sha1):
os.write(fd, file_data)
finally:
os.close(fd)
l.info("downloaded %s bytes to %s file%s:", filesize, (mimetype or "unknown"), ["", " for printing"][int(printit)])
l.info(" %s", filename)
l.info("downloaded %s bytes to %s file%s:", filesize, (mimetype or "temporary"), ["", " for printing"][int(printit)])
l.info(" '%s'", filename)
if printit:
printer = options.strget("printer")
title = options.strget("title")
if title:
l.info(" sending '%s' to printer '%s'", title, printer)
else:
l.info(" sending to printer '%s'", printer)
print_options = options.dictget("options")
#TODO: how do we print multiple copies?
#copies = options.intget("copies")
Expand All @@ -141,13 +177,8 @@ def check_digest(algo="sha1", libfn=hashlib.sha1):
self._open_file(filename)

def _print_file(self, filename, mimetype, printer, title, options):
import time
from xpra.platform.printing import print_files, printing_finished, get_printers
printers = get_printers()
if printer not in printers:
printlog.error("Error: printer '%s' does not exist!", printer)
printlog.error(" printers available: %s", csv(printers.keys()) or "none")
return
def delfile():
if not DELETE_PRINTER_FILE:
return
Expand All @@ -156,6 +187,16 @@ def delfile():
except:
printlog("failed to delete print job file '%s'", filename)
return False
if not printer:
printlog.error("Error: the printer name is missing")
printlog.error(" printers available: %s", csv(printers.keys()) or "none")
delfile()
return
if printer not in printers:
printlog.error("Error: printer '%s' does not exist!", printer)
printlog.error(" printers available: %s", csv(printers.keys()) or "none")
delfile()
return
job = print_files(printer, [filename], title, options)
printlog("printing %s, job=%s", filename, job)
if job<=0:
Expand Down Expand Up @@ -183,7 +224,6 @@ def _open_file(self, filename):
filelog.warn(" ignoring uploaded file:")
filelog.warn(" '%s'", filename)
return
import subprocess, shlex
command = shlex.split(self.open_command)+[filename]
proc = subprocess.Popen(command)
cr = getChildReaper()
Expand All @@ -194,3 +234,51 @@ def open_done(*args):
filelog.warn("Warning: failed to open the downloaded file")
filelog.warn(" '%s %s' returned %s", self.open_command, filename, returncode)
cr.add_process(proc, "Open File %s" % filename, command, True, True, open_done)

def send_file(self, filename, mimetype, data, filesize=0, printit=False, openit=False, options={}):
if printit:
if not self.printing:
printlog.warn("Warning: printing is not enabled for %s", self)
return False
if not self.remote_printing:
printlog.warn("Warning: remote end does not support printing")
return False
action = "print"
l = printlog
else:
if not self.file_transfer:
filelog.warn("Warning: file transfers are not enabled for %s", self)
return False
if not self.remote_file_transfer:
printlog.warn("Warning: remote end does not support file transfers")
return False
action = "upload"
l = filelog
l("send_file%s", (filename, mimetype, "%s bytes" % len(data), printit, openit, options))
if not printit and openit and not self.remote_open_files:
l.warn("Warning: opening the file after transfer is disabled on the remote end")
openit = False
assert len(data)>=filesize, "data is smaller then the given file size!"
data = data[:filesize] #gio may null terminate it
filelog("send_file%s", (filename, "%i bytes" % filesize, openit))
absfile = os.path.abspath(filename)
basefilename = os.path.basename(filename)
cdata = self.compressed_wrapper("file-data", data)
assert len(cdata)<=filesize #compressed wrapper ensures this is true
if filesize>self.file_size_limit*1024*1024:
filelog.warn("Warning: cannot %s the file '%s'", action, basefilename)
filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
filelog.warn(" the file size limit is %iMB", self.file_size_limit)
return False
if filesize>self.remote_file_size_limit*1024*1024:
filelog.warn("Warning: cannot %s the file '%s'", action, basefilename)
filelog.warn(" this file is too large: %sB", std_unit(filesize, unit=1024))
filelog.warn(" the remote file size limit is %iMB", self.remote_file_size_limit)
return False
mimetype = ""
u = hashlib.sha1()
u.update(data)
filelog("sha1 digest(%s)=%s", absfile, u.hexdigest())
options["sha1"] = u.hexdigest()
self.send("send-file", basefilename, mimetype, printit, openit, filesize, cdata, options)
return True
2 changes: 1 addition & 1 deletion src/xpra/platform/darwin/osx_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def set_encodings_menu(*args):
def add_ah(*args):
if self.client.start_new_commands:
actions_menu.add(self.make_startnewcommandmenuitem())
if self.client.server_file_transfer:
if self.client.remote_file_transfer:
actions_menu.add(self.make_uploadmenuitem())
self.client.after_handshake(add_ah)
menus.append(("Actions", actions_menu))
Expand Down
Loading

0 comments on commit 1352ed9

Please sign in to comment.