aboutsummaryrefslogtreecommitdiff
path: root/paramiko/sftp_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/sftp_server.py')
-rw-r--r--paramiko/sftp_server.py174
1 files changed, 89 insertions, 85 deletions
diff --git a/paramiko/sftp_server.py b/paramiko/sftp_server.py
index 1833c2e..2d8d190 100644
--- a/paramiko/sftp_server.py
+++ b/paramiko/sftp_server.py
@@ -22,46 +22,55 @@ Server-mode SFTP support.
import os
import errno
+import sys
+from hashlib import md5, sha1
-from Crypto.Hash import MD5, SHA
-from paramiko.common import *
+from paramiko import util
+from paramiko.sftp import BaseSFTP, Message, SFTP_FAILURE, \
+ SFTP_PERMISSION_DENIED, SFTP_NO_SUCH_FILE
+from paramiko.sftp_si import SFTPServerInterface
+from paramiko.sftp_attr import SFTPAttributes
+from paramiko.common import DEBUG
+from paramiko.py3compat import long, string_types, bytes_types, b
from paramiko.server import SubsystemHandler
-from paramiko.sftp import *
-from paramiko.sftp_si import *
-from paramiko.sftp_attr import *
# known hash algorithms for the "check-file" extension
+from paramiko.sftp import CMD_HANDLE, SFTP_DESC, CMD_STATUS, SFTP_EOF, CMD_NAME, \
+ SFTP_BAD_MESSAGE, CMD_EXTENDED_REPLY, SFTP_FLAG_READ, SFTP_FLAG_WRITE, \
+ SFTP_FLAG_APPEND, SFTP_FLAG_CREATE, SFTP_FLAG_TRUNC, SFTP_FLAG_EXCL, \
+ CMD_NAMES, CMD_OPEN, CMD_CLOSE, SFTP_OK, CMD_READ, CMD_DATA, CMD_WRITE, \
+ CMD_REMOVE, CMD_RENAME, CMD_MKDIR, CMD_RMDIR, CMD_OPENDIR, CMD_READDIR, \
+ CMD_STAT, CMD_ATTRS, CMD_LSTAT, CMD_FSTAT, CMD_SETSTAT, CMD_FSETSTAT, \
+ CMD_READLINK, CMD_SYMLINK, CMD_REALPATH, CMD_EXTENDED, SFTP_OP_UNSUPPORTED
+
_hash_class = {
- 'sha1': SHA,
- 'md5': MD5,
+ 'sha1': sha1,
+ 'md5': md5,
}
class SFTPServer (BaseSFTP, SubsystemHandler):
"""
- Server-side SFTP subsystem support. Since this is a L{SubsystemHandler},
- it can be (and is meant to be) set as the handler for C{"sftp"} requests.
- Use L{Transport.set_subsystem_handler} to activate this class.
+ Server-side SFTP subsystem support. Since this is a `.SubsystemHandler`,
+ it can be (and is meant to be) set as the handler for ``"sftp"`` requests.
+ Use `.Transport.set_subsystem_handler` to activate this class.
"""
def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs):
"""
The constructor for SFTPServer is meant to be called from within the
- L{Transport} as a subsystem handler. C{server} and any additional
+ `.Transport` as a subsystem handler. ``server`` and any additional
parameters or keyword parameters are passed from the original call to
- L{Transport.set_subsystem_handler}.
+ `.Transport.set_subsystem_handler`.
- @param channel: channel passed from the L{Transport}.
- @type channel: L{Channel}
- @param name: name of the requested subsystem.
- @type name: str
- @param server: the server object associated with this channel and
- subsystem
- @type server: L{ServerInterface}
- @param sftp_si: a subclass of L{SFTPServerInterface} to use for handling
- individual requests.
- @type sftp_si: class
+ :param .Channel channel: channel passed from the `.Transport`.
+ :param str name: name of the requested subsystem.
+ :param .ServerInterface server:
+ the server object associated with this channel and subsystem
+ :param class sftp_si:
+ a subclass of `.SFTPServerInterface` to use for handling individual
+ requests.
"""
BaseSFTP.__init__(self)
SubsystemHandler.__init__(self, channel, name, server)
@@ -70,17 +79,17 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
self.ultra_debug = transport.get_hexdump()
self.next_handle = 1
# map of handle-string to SFTPHandle for files & folders:
- self.file_table = { }
- self.folder_table = { }
+ self.file_table = {}
+ self.folder_table = {}
self.server = sftp_si(server, *largs, **kwargs)
-
+
def _log(self, level, msg):
if issubclass(type(msg), list):
for m in msg:
super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + m)
else:
super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + msg)
-
+
def start_subsystem(self, name, transport, channel):
self.sock = channel
self._log(DEBUG, 'Started sftp server on channel %s' % repr(channel))
@@ -92,7 +101,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
except EOFError:
self._log(DEBUG, 'EOF -- end of session')
return
- except Exception, e:
+ except Exception as e:
self._log(DEBUG, 'Exception on channel: ' + str(e))
self._log(DEBUG, util.tb_strings())
return
@@ -100,7 +109,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
request_number = msg.get_int()
try:
self._process(t, request_number, msg)
- except Exception, e:
+ except Exception as e:
self._log(DEBUG, 'Exception in server processing: ' + str(e))
self._log(DEBUG, util.tb_strings())
# send some kind of failure message, at least
@@ -113,23 +122,21 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
self.server.session_ended()
super(SFTPServer, self).finish_subsystem()
# close any file handles that were left open (so we can return them to the OS quickly)
- for f in self.file_table.itervalues():
+ for f in self.file_table.values():
f.close()
- for f in self.folder_table.itervalues():
+ for f in self.folder_table.values():
f.close()
self.file_table = {}
self.folder_table = {}
def convert_errno(e):
"""
- Convert an errno value (as from an C{OSError} or C{IOError}) into a
+ Convert an errno value (as from an ``OSError`` or ``IOError``) into a
standard SFTP result code. This is a convenience function for trapping
exceptions in server code and returning an appropriate result.
- @param e: an errno code, as from C{OSError.errno}.
- @type e: int
- @return: an SFTP error code like L{SFTP_NO_SUCH_FILE}.
- @rtype: int
+ :param int e: an errno code, as from ``OSError.errno``.
+ :return: an `int` SFTP error code like ``SFTP_NO_SUCH_FILE``.
"""
if e == errno.EACCES:
# permission denied
@@ -144,18 +151,16 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
def set_file_attr(filename, attr):
"""
Change a file's attributes on the local filesystem. The contents of
- C{attr} are used to change the permissions, owner, group ownership,
+ ``attr`` are used to change the permissions, owner, group ownership,
and/or modification & access time of the file, depending on which
- attributes are present in C{attr}.
+ attributes are present in ``attr``.
This is meant to be a handy helper function for translating SFTP file
requests into local file operations.
-
- @param filename: name of the file to alter (should usually be an
- absolute path).
- @type filename: str
- @param attr: attributes to change.
- @type attr: L{SFTPAttributes}
+
+ :param str filename:
+ name of the file to alter (should usually be an absolute path).
+ :param .SFTPAttributes attr: attributes to change.
"""
if sys.platform != 'win32':
# mode operations are meaningless on win32
@@ -166,35 +171,34 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
if attr._flags & attr.FLAG_AMTIME:
os.utime(filename, (attr.st_atime, attr.st_mtime))
if attr._flags & attr.FLAG_SIZE:
- open(filename, 'w+').truncate(attr.st_size)
+ with open(filename, 'w+') as f:
+ f.truncate(attr.st_size)
set_file_attr = staticmethod(set_file_attr)
-
### internals...
-
def _response(self, request_number, t, *arg):
msg = Message()
msg.add_int(request_number)
for item in arg:
- if type(item) is int:
- msg.add_int(item)
- elif type(item) is long:
+ if isinstance(item, long):
msg.add_int64(item)
- elif type(item) is str:
+ elif isinstance(item, int):
+ msg.add_int(item)
+ elif isinstance(item, (string_types, bytes_types)):
msg.add_string(item)
elif type(item) is SFTPAttributes:
item._pack(msg)
else:
raise Exception('unknown type for ' + repr(item) + ' type ' + repr(type(item)))
- self._send_packet(t, str(msg))
+ self._send_packet(t, msg)
def _send_handle_response(self, request_number, handle, folder=False):
if not issubclass(type(handle), SFTPHandle):
# must be error code
self._send_status(request_number, handle)
return
- handle._set_name('hx%d' % self.next_handle)
+ handle._set_name(b('hx%d' % self.next_handle))
self.next_handle += 1
if folder:
self.folder_table[handle._get_name()] = handle
@@ -232,16 +236,16 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
msg.add_int(len(flist))
for attr in flist:
msg.add_string(attr.filename)
- msg.add_string(str(attr))
+ msg.add_string(attr)
attr._pack(msg)
- self._send_packet(CMD_NAME, str(msg))
+ self._send_packet(CMD_NAME, msg)
def _check_file(self, request_number, msg):
# this extension actually comes from v6 protocol, but since it's an
# extension, i feel like we can reasonably support it backported.
# it's very useful for verifying uploaded files or checking for
# rsync-like differences between local and remote files.
- handle = msg.get_string()
+ handle = msg.get_binary()
alg_list = msg.get_list()
start = msg.get_int64()
length = msg.get_int64()
@@ -270,17 +274,17 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
self._send_status(request_number, SFTP_FAILURE, 'Block size too small')
return
- sum_out = ''
+ sum_out = bytes()
offset = start
while offset < start + length:
blocklen = min(block_size, start + length - offset)
# don't try to read more than about 64KB at a time
chunklen = min(blocklen, 65536)
count = 0
- hash_obj = alg.new()
+ hash_obj = alg()
while count < blocklen:
data = f.read(offset, chunklen)
- if not type(data) is str:
+ if not isinstance(data, bytes_types):
self._send_status(request_number, data, 'Unable to hash file')
return
hash_obj.update(data)
@@ -293,10 +297,10 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
msg.add_string('check-file')
msg.add_string(algname)
msg.add_bytes(sum_out)
- self._send_packet(CMD_EXTENDED_REPLY, str(msg))
-
+ self._send_packet(CMD_EXTENDED_REPLY, msg)
+
def _convert_pflags(self, pflags):
- "convert SFTP-style open() flags to python's os.open() flags"
+ """convert SFTP-style open() flags to Python's os.open() flags"""
if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE):
flags = os.O_RDWR
elif pflags & SFTP_FLAG_WRITE:
@@ -316,12 +320,12 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
def _process(self, t, request_number, msg):
self._log(DEBUG, 'Request: %s' % CMD_NAMES[t])
if t == CMD_OPEN:
- path = msg.get_string()
+ path = msg.get_text()
flags = self._convert_pflags(msg.get_int())
attr = SFTPAttributes._from_msg(msg)
self._send_handle_response(request_number, self.server.open(path, flags, attr))
elif t == CMD_CLOSE:
- handle = msg.get_string()
+ handle = msg.get_binary()
if handle in self.folder_table:
del self.folder_table[handle]
self._send_status(request_number, SFTP_OK)
@@ -333,14 +337,14 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
return
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
elif t == CMD_READ:
- handle = msg.get_string()
+ handle = msg.get_binary()
offset = msg.get_int64()
length = msg.get_int()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
data = self.file_table[handle].read(offset, length)
- if type(data) is str:
+ if isinstance(data, (bytes_types, string_types)):
if len(data) == 0:
self._send_status(request_number, SFTP_EOF)
else:
@@ -348,54 +352,54 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
else:
self._send_status(request_number, data)
elif t == CMD_WRITE:
- handle = msg.get_string()
+ handle = msg.get_binary()
offset = msg.get_int64()
- data = msg.get_string()
+ data = msg.get_binary()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
self._send_status(request_number, self.file_table[handle].write(offset, data))
elif t == CMD_REMOVE:
- path = msg.get_string()
+ path = msg.get_text()
self._send_status(request_number, self.server.remove(path))
elif t == CMD_RENAME:
- oldpath = msg.get_string()
- newpath = msg.get_string()
+ oldpath = msg.get_text()
+ newpath = msg.get_text()
self._send_status(request_number, self.server.rename(oldpath, newpath))
elif t == CMD_MKDIR:
- path = msg.get_string()
+ path = msg.get_text()
attr = SFTPAttributes._from_msg(msg)
self._send_status(request_number, self.server.mkdir(path, attr))
elif t == CMD_RMDIR:
- path = msg.get_string()
+ path = msg.get_text()
self._send_status(request_number, self.server.rmdir(path))
elif t == CMD_OPENDIR:
- path = msg.get_string()
+ path = msg.get_text()
self._open_folder(request_number, path)
return
elif t == CMD_READDIR:
- handle = msg.get_string()
+ handle = msg.get_binary()
if handle not in self.folder_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
folder = self.folder_table[handle]
self._read_folder(request_number, folder)
elif t == CMD_STAT:
- path = msg.get_string()
+ path = msg.get_text()
resp = self.server.stat(path)
if issubclass(type(resp), SFTPAttributes):
self._response(request_number, CMD_ATTRS, resp)
else:
self._send_status(request_number, resp)
elif t == CMD_LSTAT:
- path = msg.get_string()
+ path = msg.get_text()
resp = self.server.lstat(path)
if issubclass(type(resp), SFTPAttributes):
self._response(request_number, CMD_ATTRS, resp)
else:
self._send_status(request_number, resp)
elif t == CMD_FSTAT:
- handle = msg.get_string()
+ handle = msg.get_binary()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
@@ -405,34 +409,34 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
else:
self._send_status(request_number, resp)
elif t == CMD_SETSTAT:
- path = msg.get_string()
+ path = msg.get_text()
attr = SFTPAttributes._from_msg(msg)
self._send_status(request_number, self.server.chattr(path, attr))
elif t == CMD_FSETSTAT:
- handle = msg.get_string()
+ handle = msg.get_binary()
attr = SFTPAttributes._from_msg(msg)
if handle not in self.file_table:
self._response(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
self._send_status(request_number, self.file_table[handle].chattr(attr))
elif t == CMD_READLINK:
- path = msg.get_string()
+ path = msg.get_text()
resp = self.server.readlink(path)
- if type(resp) is str:
+ if isinstance(resp, (bytes_types, string_types)):
self._response(request_number, CMD_NAME, 1, resp, '', SFTPAttributes())
else:
self._send_status(request_number, resp)
elif t == CMD_SYMLINK:
# the sftp 2 draft is incorrect here! path always follows target_path
- target_path = msg.get_string()
- path = msg.get_string()
+ target_path = msg.get_text()
+ path = msg.get_text()
self._send_status(request_number, self.server.symlink(target_path, path))
elif t == CMD_REALPATH:
- path = msg.get_string()
+ path = msg.get_text()
rpath = self.server.canonicalize(path)
self._response(request_number, CMD_NAME, 1, rpath, '', SFTPAttributes())
elif t == CMD_EXTENDED:
- tag = msg.get_string()
+ tag = msg.get_text()
if tag == 'check-file':
self._check_file(request_number, msg)
else: