diff options
Diffstat (limited to 'paramiko/sftp.py')
-rw-r--r-- | paramiko/sftp.py | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/paramiko/sftp.py b/paramiko/sftp.py new file mode 100644 index 0000000..58d7103 --- /dev/null +++ b/paramiko/sftp.py @@ -0,0 +1,168 @@ +# Copyright (C) 2003-2005 Robey Pointer <robey@lag.net> +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +import socket +import struct + +from paramiko.common import * +from paramiko import util +from paramiko.channel import Channel +from paramiko.message import Message + + +CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, CMD_FSTAT, \ + CMD_SETSTAT, CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, CMD_REMOVE, CMD_MKDIR, \ + CMD_RMDIR, CMD_REALPATH, CMD_STAT, CMD_RENAME, CMD_READLINK, CMD_SYMLINK \ + = range(1, 21) +CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS = range(101, 106) +CMD_EXTENDED, CMD_EXTENDED_REPLY = range(200, 202) + +SFTP_OK = 0 +SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, SFTP_BAD_MESSAGE, \ + SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED = range(1, 9) + +SFTP_DESC = [ 'Success', + 'End of file', + 'No such file', + 'Permission denied', + 'Failure', + 'Bad message', + 'No connection', + 'Connection lost', + 'Operation unsupported' ] + +SFTP_FLAG_READ = 0x1 +SFTP_FLAG_WRITE = 0x2 +SFTP_FLAG_APPEND = 0x4 +SFTP_FLAG_CREATE = 0x8 +SFTP_FLAG_TRUNC = 0x10 +SFTP_FLAG_EXCL = 0x20 + +_VERSION = 3 + + +# for debugging +CMD_NAMES = { + CMD_INIT: 'init', + CMD_VERSION: 'version', + CMD_OPEN: 'open', + CMD_CLOSE: 'close', + CMD_READ: 'read', + CMD_WRITE: 'write', + CMD_LSTAT: 'lstat', + CMD_FSTAT: 'fstat', + CMD_SETSTAT: 'setstat', + CMD_FSETSTAT: 'fsetstat', + CMD_OPENDIR: 'opendir', + CMD_READDIR: 'readdir', + CMD_REMOVE: 'remove', + CMD_MKDIR: 'mkdir', + CMD_RMDIR: 'rmdir', + CMD_REALPATH: 'realpath', + CMD_STAT: 'stat', + CMD_RENAME: 'rename', + CMD_READLINK: 'readlink', + CMD_SYMLINK: 'symlink', + CMD_STATUS: 'status', + CMD_HANDLE: 'handle', + CMD_DATA: 'data', + CMD_NAME: 'name', + CMD_ATTRS: 'attrs', + CMD_EXTENDED: 'extended', + CMD_EXTENDED_REPLY: 'extended_reply' + } + + +class SFTPError (Exception): + pass + + +class BaseSFTP (object): + def __init__(self): + self.logger = util.get_logger('paramiko.sftp') + self.sock = None + self.ultra_debug = False + + + ### internals... + + + def _send_version(self): + self._send_packet(CMD_INIT, struct.pack('>I', _VERSION)) + t, data = self._read_packet() + if t != CMD_VERSION: + raise SFTPError('Incompatible sftp protocol') + version = struct.unpack('>I', data[:4])[0] + # if version != _VERSION: + # raise SFTPError('Incompatible sftp protocol') + return version + + def _send_server_version(self): + # advertise that we support "check-file" + extension_pairs = [ 'check-file', 'md5,sha1' ] + msg = Message() + msg.add_int(_VERSION) + msg.add(*extension_pairs) + self._send_packet(CMD_VERSION, str(msg)) + t, data = self._read_packet() + if t != CMD_INIT: + raise SFTPError('Incompatible sftp protocol') + version = struct.unpack('>I', data[:4])[0] + return version + + def _log(self, level, msg): + if issubclass(type(msg), list): + for m in msg: + self.logger.log(level, m) + else: + self.logger.log(level, msg) + + def _write_all(self, out): + while len(out) > 0: + n = self.sock.send(out) + if n <= 0: + raise EOFError() + if n == len(out): + return + out = out[n:] + return + + def _read_all(self, n): + out = '' + while n > 0: + x = self.sock.recv(n) + if len(x) == 0: + raise EOFError() + out += x + n -= len(x) + return out + + def _send_packet(self, t, packet): + out = struct.pack('>I', len(packet) + 1) + chr(t) + packet + if self.ultra_debug: + self._log(DEBUG, util.format_binary(out, 'OUT: ')) + self._write_all(out) + + def _read_packet(self): + size = struct.unpack('>I', self._read_all(4))[0] + data = self._read_all(size) + if self.ultra_debug: + self._log(DEBUG, util.format_binary(data, 'IN: ')); + if size > 0: + return ord(data[0]), data[1:] + return 0, '' |