From ed280d5ac360e2af796e9bd973d7b4df89f0c449 Mon Sep 17 00:00:00 2001 From: "Jeremy T. Bouse" Date: Fri, 27 Nov 2009 16:20:12 -0500 Subject: Imported Upstream version 1.7.4 --- docs/paramiko.sftp_server-pysrc.html | 1196 ++++++++++++++++++++++++++++++++++ 1 file changed, 1196 insertions(+) create mode 100644 docs/paramiko.sftp_server-pysrc.html (limited to 'docs/paramiko.sftp_server-pysrc.html') diff --git a/docs/paramiko.sftp_server-pysrc.html b/docs/paramiko.sftp_server-pysrc.html new file mode 100644 index 0000000..cd77999 --- /dev/null +++ b/docs/paramiko.sftp_server-pysrc.html @@ -0,0 +1,1196 @@ + + + + + paramiko.sftp_server + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Package paramiko :: + Module sftp_server + + + + + +
[frames] | no frames]
+
+

Source Code for Module paramiko.sftp_server

+
+  1  # Copyright (C) 2003-2007  Robey Pointer <robey@lag.net> 
+  2  # 
+  3  # This file is part of paramiko. 
+  4  # 
+  5  # Paramiko is free software; you can redistribute it and/or modify it under the 
+  6  # terms of the GNU Lesser General Public License as published by the Free 
+  7  # Software Foundation; either version 2.1 of the License, or (at your option) 
+  8  # any later version. 
+  9  # 
+ 10  # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 
+ 11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
+ 12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
+ 13  # details. 
+ 14  # 
+ 15  # You should have received a copy of the GNU Lesser General Public License 
+ 16  # along with Paramiko; if not, write to the Free Software Foundation, Inc., 
+ 17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
+ 18   
+ 19  """ 
+ 20  Server-mode SFTP support. 
+ 21  """ 
+ 22   
+ 23  import os 
+ 24  import errno 
+ 25   
+ 26  from Crypto.Hash import MD5, SHA 
+ 27  from paramiko.common import * 
+ 28  from paramiko.server import SubsystemHandler 
+ 29  from paramiko.sftp import * 
+ 30  from paramiko.sftp_si import * 
+ 31  from paramiko.sftp_attr import * 
+ 32   
+ 33   
+ 34  # known hash algorithms for the "check-file" extension 
+ 35  _hash_class = { 
+ 36      'sha1': SHA, 
+ 37      'md5': MD5, 
+ 38  } 
+ 39   
+ 40   
+
41 -class SFTPServer (BaseSFTP, SubsystemHandler): +
42 """ + 43 Server-side SFTP subsystem support. Since this is a L{SubsystemHandler}, + 44 it can be (and is meant to be) set as the handler for C{"sftp"} requests. + 45 Use L{Transport.set_subsystem_handler} to activate this class. + 46 """ + 47 +
48 - def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs): +
49 """ + 50 The constructor for SFTPServer is meant to be called from within the + 51 L{Transport} as a subsystem handler. C{server} and any additional + 52 parameters or keyword parameters are passed from the original call to + 53 L{Transport.set_subsystem_handler}. + 54 + 55 @param channel: channel passed from the L{Transport}. + 56 @type channel: L{Channel} + 57 @param name: name of the requested subsystem. + 58 @type name: str + 59 @param server: the server object associated with this channel and + 60 subsystem + 61 @type server: L{ServerInterface} + 62 @param sftp_si: a subclass of L{SFTPServerInterface} to use for handling + 63 individual requests. + 64 @type sftp_si: class + 65 """ + 66 BaseSFTP.__init__(self) + 67 SubsystemHandler.__init__(self, channel, name, server) + 68 transport = channel.get_transport() + 69 self.logger = util.get_logger(transport.get_log_channel() + '.sftp') + 70 self.ultra_debug = transport.get_hexdump() + 71 self.next_handle = 1 + 72 # map of handle-string to SFTPHandle for files & folders: + 73 self.file_table = { } + 74 self.folder_table = { } + 75 self.server = sftp_si(server, *largs, **kwargs) +
76 +
77 - def _log(self, level, msg): +
78 if issubclass(type(msg), list): + 79 for m in msg: + 80 super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + m) + 81 else: + 82 super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + msg) +
83 +
84 - def start_subsystem(self, name, transport, channel): +
85 self.sock = channel + 86 self._log(DEBUG, 'Started sftp server on channel %s' % repr(channel)) + 87 self._send_server_version() + 88 self.server.session_started() + 89 while True: + 90 try: + 91 t, data = self._read_packet() + 92 except EOFError: + 93 self._log(DEBUG, 'EOF -- end of session') + 94 return + 95 except Exception, e: + 96 self._log(DEBUG, 'Exception on channel: ' + str(e)) + 97 self._log(DEBUG, util.tb_strings()) + 98 return + 99 msg = Message(data) +100 request_number = msg.get_int() +101 try: +102 self._process(t, request_number, msg) +103 except Exception, e: +104 self._log(DEBUG, 'Exception in server processing: ' + str(e)) +105 self._log(DEBUG, util.tb_strings()) +106 # send some kind of failure message, at least +107 try: +108 self._send_status(request_number, SFTP_FAILURE) +109 except: +110 pass +
111 +
112 - def finish_subsystem(self): +
113 self.server.session_ended() +114 super(SFTPServer, self).finish_subsystem() +115 # close any file handles that were left open (so we can return them to the OS quickly) +116 for f in self.file_table.itervalues(): +117 f.close() +118 for f in self.folder_table.itervalues(): +119 f.close() +120 self.file_table = {} +121 self.folder_table = {} +
122 +
123 - def convert_errno(e): +
124 """ +125 Convert an errno value (as from an C{OSError} or C{IOError}) into a +126 standard SFTP result code. This is a convenience function for trapping +127 exceptions in server code and returning an appropriate result. +128 +129 @param e: an errno code, as from C{OSError.errno}. +130 @type e: int +131 @return: an SFTP error code like L{SFTP_NO_SUCH_FILE}. +132 @rtype: int +133 """ +134 if e == errno.EACCES: +135 # permission denied +136 return SFTP_PERMISSION_DENIED +137 elif (e == errno.ENOENT) or (e == errno.ENOTDIR): +138 # no such file +139 return SFTP_NO_SUCH_FILE +140 else: +141 return SFTP_FAILURE +
142 convert_errno = staticmethod(convert_errno) +143 +
144 - def set_file_attr(filename, attr): +
145 """ +146 Change a file's attributes on the local filesystem. The contents of +147 C{attr} are used to change the permissions, owner, group ownership, +148 and/or modification & access time of the file, depending on which +149 attributes are present in C{attr}. +150 +151 This is meant to be a handy helper function for translating SFTP file +152 requests into local file operations. +153 +154 @param filename: name of the file to alter (should usually be an +155 absolute path). +156 @type filename: str +157 @param attr: attributes to change. +158 @type attr: L{SFTPAttributes} +159 """ +160 if sys.platform != 'win32': +161 # mode operations are meaningless on win32 +162 if attr._flags & attr.FLAG_PERMISSIONS: +163 os.chmod(filename, attr.st_mode) +164 if attr._flags & attr.FLAG_UIDGID: +165 os.chown(filename, attr.st_uid, attr.st_gid) +166 if attr._flags & attr.FLAG_AMTIME: +167 os.utime(filename, (attr.st_atime, attr.st_mtime)) +168 if attr._flags & attr.FLAG_SIZE: +169 open(filename, 'w+').truncate(attr.st_size) +
170 set_file_attr = staticmethod(set_file_attr) +171 +172 +173 ### internals... +174 +175 +
176 - def _response(self, request_number, t, *arg): +
177 msg = Message() +178 msg.add_int(request_number) +179 for item in arg: +180 if type(item) is int: +181 msg.add_int(item) +182 elif type(item) is long: +183 msg.add_int64(item) +184 elif type(item) is str: +185 msg.add_string(item) +186 elif type(item) is SFTPAttributes: +187 item._pack(msg) +188 else: +189 raise Exception('unknown type for ' + repr(item) + ' type ' + repr(type(item))) +190 self._send_packet(t, str(msg)) +
191 +
192 - def _send_handle_response(self, request_number, handle, folder=False): +
193 if not issubclass(type(handle), SFTPHandle): +194 # must be error code +195 self._send_status(request_number, handle) +196 return +197 handle._set_name('hx%d' % self.next_handle) +198 self.next_handle += 1 +199 if folder: +200 self.folder_table[handle._get_name()] = handle +201 else: +202 self.file_table[handle._get_name()] = handle +203 self._response(request_number, CMD_HANDLE, handle._get_name()) +
204 +
205 - def _send_status(self, request_number, code, desc=None): +
206 if desc is None: +207 try: +208 desc = SFTP_DESC[code] +209 except IndexError: +210 desc = 'Unknown' +211 # some clients expect a "langauge" tag at the end (but don't mind it being blank) +212 self._response(request_number, CMD_STATUS, code, desc, '') +
213 +
214 - def _open_folder(self, request_number, path): +
215 resp = self.server.list_folder(path) +216 if issubclass(type(resp), list): +217 # got an actual list of filenames in the folder +218 folder = SFTPHandle() +219 folder._set_files(resp) +220 self._send_handle_response(request_number, folder, True) +221 return +222 # must be an error code +223 self._send_status(request_number, resp) +
224 +
225 - def _read_folder(self, request_number, folder): +
226 flist = folder._get_next_files() +227 if len(flist) == 0: +228 self._send_status(request_number, SFTP_EOF) +229 return +230 msg = Message() +231 msg.add_int(request_number) +232 msg.add_int(len(flist)) +233 for attr in flist: +234 msg.add_string(attr.filename) +235 msg.add_string(str(attr)) +236 attr._pack(msg) +237 self._send_packet(CMD_NAME, str(msg)) +
238 +
239 - def _check_file(self, request_number, msg): +
240 # this extension actually comes from v6 protocol, but since it's an +241 # extension, i feel like we can reasonably support it backported. +242 # it's very useful for verifying uploaded files or checking for +243 # rsync-like differences between local and remote files. +244 handle = msg.get_string() +245 alg_list = msg.get_list() +246 start = msg.get_int64() +247 length = msg.get_int64() +248 block_size = msg.get_int() +249 if handle not in self.file_table: +250 self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +251 return +252 f = self.file_table[handle] +253 for x in alg_list: +254 if x in _hash_class: +255 algname = x +256 alg = _hash_class[x] +257 break +258 else: +259 self._send_status(request_number, SFTP_FAILURE, 'No supported hash types found') +260 return +261 if length == 0: +262 st = f.stat() +263 if not issubclass(type(st), SFTPAttributes): +264 self._send_status(request_number, st, 'Unable to stat file') +265 return +266 length = st.st_size - start +267 if block_size == 0: +268 block_size = length +269 if block_size < 256: +270 self._send_status(request_number, SFTP_FAILURE, 'Block size too small') +271 return +272 +273 sum_out = '' +274 offset = start +275 while offset < start + length: +276 blocklen = min(block_size, start + length - offset) +277 # don't try to read more than about 64KB at a time +278 chunklen = min(blocklen, 65536) +279 count = 0 +280 hash_obj = alg.new() +281 while count < blocklen: +282 data = f.read(offset, chunklen) +283 if not type(data) is str: +284 self._send_status(request_number, data, 'Unable to hash file') +285 return +286 hash_obj.update(data) +287 count += len(data) +288 offset += count +289 sum_out += hash_obj.digest() +290 +291 msg = Message() +292 msg.add_int(request_number) +293 msg.add_string('check-file') +294 msg.add_string(algname) +295 msg.add_bytes(sum_out) +296 self._send_packet(CMD_EXTENDED_REPLY, str(msg)) +
297 +
298 - def _convert_pflags(self, pflags): +
299 "convert SFTP-style open() flags to python's os.open() flags" +300 if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE): +301 flags = os.O_RDWR +302 elif pflags & SFTP_FLAG_WRITE: +303 flags = os.O_WRONLY +304 else: +305 flags = os.O_RDONLY +306 if pflags & SFTP_FLAG_APPEND: +307 flags |= os.O_APPEND +308 if pflags & SFTP_FLAG_CREATE: +309 flags |= os.O_CREAT +310 if pflags & SFTP_FLAG_TRUNC: +311 flags |= os.O_TRUNC +312 if pflags & SFTP_FLAG_EXCL: +313 flags |= os.O_EXCL +314 return flags +
315 +
316 - def _process(self, t, request_number, msg): +
317 self._log(DEBUG, 'Request: %s' % CMD_NAMES[t]) +318 if t == CMD_OPEN: +319 path = msg.get_string() +320 flags = self._convert_pflags(msg.get_int()) +321 attr = SFTPAttributes._from_msg(msg) +322 self._send_handle_response(request_number, self.server.open(path, flags, attr)) +323 elif t == CMD_CLOSE: +324 handle = msg.get_string() +325 if handle in self.folder_table: +326 del self.folder_table[handle] +327 self._send_status(request_number, SFTP_OK) +328 return +329 if handle in self.file_table: +330 self.file_table[handle].close() +331 del self.file_table[handle] +332 self._send_status(request_number, SFTP_OK) +333 return +334 self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +335 elif t == CMD_READ: +336 handle = msg.get_string() +337 offset = msg.get_int64() +338 length = msg.get_int() +339 if handle not in self.file_table: +340 self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +341 return +342 data = self.file_table[handle].read(offset, length) +343 if type(data) is str: +344 if len(data) == 0: +345 self._send_status(request_number, SFTP_EOF) +346 else: +347 self._response(request_number, CMD_DATA, data) +348 else: +349 self._send_status(request_number, data) +350 elif t == CMD_WRITE: +351 handle = msg.get_string() +352 offset = msg.get_int64() +353 data = msg.get_string() +354 if handle not in self.file_table: +355 self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +356 return +357 self._send_status(request_number, self.file_table[handle].write(offset, data)) +358 elif t == CMD_REMOVE: +359 path = msg.get_string() +360 self._send_status(request_number, self.server.remove(path)) +361 elif t == CMD_RENAME: +362 oldpath = msg.get_string() +363 newpath = msg.get_string() +364 self._send_status(request_number, self.server.rename(oldpath, newpath)) +365 elif t == CMD_MKDIR: +366 path = msg.get_string() +367 attr = SFTPAttributes._from_msg(msg) +368 self._send_status(request_number, self.server.mkdir(path, attr)) +369 elif t == CMD_RMDIR: +370 path = msg.get_string() +371 self._send_status(request_number, self.server.rmdir(path)) +372 elif t == CMD_OPENDIR: +373 path = msg.get_string() +374 self._open_folder(request_number, path) +375 return +376 elif t == CMD_READDIR: +377 handle = msg.get_string() +378 if handle not in self.folder_table: +379 self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +380 return +381 folder = self.folder_table[handle] +382 self._read_folder(request_number, folder) +383 elif t == CMD_STAT: +384 path = msg.get_string() +385 resp = self.server.stat(path) +386 if issubclass(type(resp), SFTPAttributes): +387 self._response(request_number, CMD_ATTRS, resp) +388 else: +389 self._send_status(request_number, resp) +390 elif t == CMD_LSTAT: +391 path = msg.get_string() +392 resp = self.server.lstat(path) +393 if issubclass(type(resp), SFTPAttributes): +394 self._response(request_number, CMD_ATTRS, resp) +395 else: +396 self._send_status(request_number, resp) +397 elif t == CMD_FSTAT: +398 handle = msg.get_string() +399 if handle not in self.file_table: +400 self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +401 return +402 resp = self.file_table[handle].stat() +403 if issubclass(type(resp), SFTPAttributes): +404 self._response(request_number, CMD_ATTRS, resp) +405 else: +406 self._send_status(request_number, resp) +407 elif t == CMD_SETSTAT: +408 path = msg.get_string() +409 attr = SFTPAttributes._from_msg(msg) +410 self._send_status(request_number, self.server.chattr(path, attr)) +411 elif t == CMD_FSETSTAT: +412 handle = msg.get_string() +413 attr = SFTPAttributes._from_msg(msg) +414 if handle not in self.file_table: +415 self._response(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') +416 return +417 self._send_status(request_number, self.file_table[handle].chattr(attr)) +418 elif t == CMD_READLINK: +419 path = msg.get_string() +420 resp = self.server.readlink(path) +421 if type(resp) is str: +422 self._response(request_number, CMD_NAME, 1, resp, '', SFTPAttributes()) +423 else: +424 self._send_status(request_number, resp) +425 elif t == CMD_SYMLINK: +426 # the sftp 2 draft is incorrect here! path always follows target_path +427 target_path = msg.get_string() +428 path = msg.get_string() +429 self._send_status(request_number, self.server.symlink(target_path, path)) +430 elif t == CMD_REALPATH: +431 path = msg.get_string() +432 rpath = self.server.canonicalize(path) +433 self._response(request_number, CMD_NAME, 1, rpath, '', SFTPAttributes()) +434 elif t == CMD_EXTENDED: +435 tag = msg.get_string() +436 if tag == 'check-file': +437 self._check_file(request_number, msg) +438 else: +439 self._send_status(request_number, SFTP_OP_UNSUPPORTED) +440 else: +441 self._send_status(request_number, SFTP_OP_UNSUPPORTED) +
442 +443 +444 from paramiko.sftp_handle import SFTPHandle +445 +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + -- cgit v1.2.3