summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobey Pointer <robey@lag.net>2004-09-07 06:51:03 +0000
committerRobey Pointer <robey@lag.net>2004-09-07 06:51:03 +0000
commit574c0dd3682a4fa850efc4fde8a55869ded59b5f (patch)
tree22a23bced2bfa2dc470c3f8aeab20b25922b3a1d
parent4cbbc57c6b68cdc731e55b8e81b38bec8da4aab3 (diff)
[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-75]
clean up SFTPAttributes add english descriptions to the FX_* error codes of sftp. clean up (and document) SFTPAttributes since it's exported now, and make it simple to generate one from a python os.stat object. make "_pythonize" the default -- that is, just use the same field names as python does for os.stat. (i'm not sure why i didn't do it that way in the first place; probably ignorance.) also add str() method that converts the SFTPAttributes into a string suitable for use in ls (used in an obscure way in sftp servers).
-rw-r--r--paramiko/sftp.py231
1 files changed, 164 insertions, 67 deletions
diff --git a/paramiko/sftp.py b/paramiko/sftp.py
index 6a44b2db..f7ed3762 100644
--- a/paramiko/sftp.py
+++ b/paramiko/sftp.py
@@ -18,7 +18,7 @@
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-import struct, socket
+import struct, socket, stat, time
from common import *
import util
from channel import Channel
@@ -36,6 +36,16 @@ FX_OK = 0
FX_EOF, FX_NO_SUCH_FILE, FX_PERMISSION_DENIED, FX_FAILURE, FX_BAD_MESSAGE, \
FX_NO_CONNECTION, FX_CONNECTION_LOST, FX_OP_UNSUPPORTED = range(1, 9)
+FX_DESC = [ 'Success',
+ 'End of file',
+ 'No such file',
+ 'Permission denied',
+ 'Failure',
+ 'Bad message',
+ 'No connection',
+ 'Connection lost',
+ 'Operation unsupported' ]
+
FXF_READ = 0x1
FXF_WRITE = 0x2
FXF_APPEND = 0x4
@@ -47,81 +57,160 @@ _VERSION = 3
class SFTPAttributes (object):
-
+ """
+ Representation of the attributes of a file (or proxied file) for SFTP in
+ client or server mode. It attemps to mirror the object returned by
+ C{os.stat} as closely as possible, so it may have the following fields:
+ - st_size
+ - st_uid
+ - st_gid
+ - st_mode
+ - st_atime
+ - st_mtime
+
+ Because SFTP allows flags to have other arbitrary named attributes, these
+ are stored in a dict named C{attr}.
+ """
+
FLAG_SIZE = 1
FLAG_UIDGID = 2
FLAG_PERMISSIONS = 4
FLAG_AMTIME = 8
FLAG_EXTENDED = 0x80000000L
- def __init__(self, msg=None):
- self.flags = 0
+ def __init__(self):
+ """
+ Create a new (empty) SFTPAttributes object. All fields will be empty.
+ """
+ self._flags = 0
self.attr = {}
- if msg is not None:
- self.unpack(msg)
-
- def unpack(self, msg):
- self.flags = msg.get_int()
- if self.flags & self.FLAG_SIZE:
- self.size = msg.get_int64()
- if self.flags & self.FLAG_UIDGID:
- self.uid = msg.get_int()
- self.gid = msg.get_int()
- if self.flags & self.FLAG_PERMISSIONS:
- self.permissions = msg.get_int()
- if self.flags & self.FLAG_AMTIME:
- self.atime = msg.get_int()
- self.mtime = msg.get_int()
- if self.flags & self.FLAG_EXTENDED:
+
+ def from_stat(cls, obj):
+ """
+ Create an SFTPAttributes object from an existing C{stat} object (an
+ object returned by C{os.stat}).
+
+ @param obj: an object returned by C{os.stat} (or equivalent).
+ @type obj: object
+ @return: new L{SFTPAttributes} object with the same attribute fields.
+ @rtype: L{SFTPAttributes}
+ """
+ attr = cls()
+ attr.st_size = obj.st_size
+ attr.st_uid = obj.st_uid
+ attr.st_gid = obj.st_gid
+ attr.st_mode = obj.st_mode
+ attr.st_atime = obj.st_atime
+ attr.st_mtime = obj.st_mtime
+ return attr
+ from_stat = classmethod(from_stat)
+
+
+ ### internals...
+
+
+ def _from_msg(cls, msg):
+ attr = cls()
+ attr._unpack(msg)
+ return attr
+ _from_msg = classmethod(_from_msg)
+
+ def _unpack(self, msg):
+ self._flags = msg.get_int()
+ if self._flags & self.FLAG_SIZE:
+ self.st_size = msg.get_int64()
+ if self._flags & self.FLAG_UIDGID:
+ self.st_uid = msg.get_int()
+ self.st_gid = msg.get_int()
+ if self._flags & self.FLAG_PERMISSIONS:
+ self.st_mode = msg.get_int()
+ if self._flags & self.FLAG_AMTIME:
+ self.st_atime = msg.get_int()
+ self.st_mtime = msg.get_int()
+ if self._flags & self.FLAG_EXTENDED:
count = msg.get_int()
for i in range(count):
self.attr[msg.get_string()] = msg.get_string()
return msg.get_remainder()
- def pack(self, msg):
- self.flags = 0
- if hasattr(self, 'size'):
- self.flags |= self.FLAG_SIZE
- if hasattr(self, 'uid') or hasattr(self, 'gid'):
- self.flags |= self.FLAG_UIDGID
- if hasattr(self, 'permissions'):
- self.flags |= self.FLAG_PERMISSIONS
- if hasattr(self, 'atime') or hasattr(self, 'mtime'):
- self.flags |= self.FLAG_AMTIME
+ def _pack(self, msg):
+ self._flags = 0
+ if hasattr(self, 'st_size'):
+ self._flags |= self.FLAG_SIZE
+ if hasattr(self, 'st_uid') or hasattr(self, 'st_gid'):
+ self._flags |= self.FLAG_UIDGID
+ if hasattr(self, 'st_mode'):
+ self._flags |= self.FLAG_PERMISSIONS
+ if hasattr(self, 'st_atime') or hasattr(self, 'st_mtime'):
+ self._flags |= self.FLAG_AMTIME
if len(self.attr) > 0:
- self.flags |= self.FLAG_EXTENDED
- msg.add_int(self.flags)
- if self.flags & self.FLAG_SIZE:
- msg.add_int64(self.size)
- if self.flags & self.FLAG_UIDGID:
- msg.add_int(getattr(self, 'uid', 0))
- msg.add_int(getattr(self, 'gid', 0))
- if self.flags & self.FLAG_PERMISSIONS:
- msg.add_int(self.permissions)
- if self.flags & self.FLAG_AMTIME:
- msg.add_int(getattr(self, 'atime', 0))
- msg.add_int(getattr(self, 'mtime', 0))
- if self.flags & self.FLAG_EXTENDED:
+ self._flags |= self.FLAG_EXTENDED
+ msg.add_int(self._flags)
+ if self._flags & self.FLAG_SIZE:
+ msg.add_int64(self.st_size)
+ if self._flags & self.FLAG_UIDGID:
+ msg.add_int(getattr(self, 'st_uid', 0))
+ msg.add_int(getattr(self, 'st_gid', 0))
+ if self._flags & self.FLAG_PERMISSIONS:
+ msg.add_int(self.st_mode)
+ if self._flags & self.FLAG_AMTIME:
+ msg.add_int(getattr(self, 'st_atime', 0))
+ msg.add_int(getattr(self, 'st_mtime', 0))
+ if self._flags & self.FLAG_EXTENDED:
msg.add_int(len(self.attr))
- for key, val in self.attr:
+ for key, val in self.attr.iteritems():
msg.add_string(key)
msg.add_string(val)
return
- def _pythonize(self):
- "create attributes named the way python's os.stat does it"
- if hasattr(self, 'size'):
- self.st_size = self.size
- if hasattr(self, 'uid'):
- self.st_uid = self.uid
- if hasattr(self, 'gid'):
- self.st_gid = self.gid
+ def _rwx(n, suid, sticky=False):
+ if suid:
+ suid = 2
+ out = '-r'[n >> 2] + '-w'[(n >> 1) & 1]
+ if sticky:
+ out += '-xTt'[suid + (n & 1)]
+ else:
+ out += '-xSs'[suid + (n & 1)]
+ return out
+ _rwx = staticmethod(_rwx)
+
+ def __str__(self):
+ "create a unix-style long description of the file (like ls -l)"
if hasattr(self, 'permissions'):
- self.st_mode = self.permissions
- if hasattr(self, 'atime'):
- self.st_atime = self.atime
- if hasattr(self, 'mtime'):
- self.st_mtime = self.mtime
+ kind = self.permissions & stat.S_IFMT
+ if kind == stat.S_IFIFO:
+ ks = 'p'
+ elif kind == stat.S_IFCHR:
+ ks = 'c'
+ elif kind == stat.S_IFDIR:
+ ks = 'd'
+ elif kind == stat.S_IFBLK:
+ ks = 'b'
+ elif kind == stat.S_IFREG:
+ ks = '-'
+ elif kind == stat.S_IFLNK:
+ ks = 'l'
+ elif kind == stat.S_IFSOCK:
+ ks = 's'
+ else:
+ ks = '?'
+ ks += _rwx((self.permissions & 0700) >> 6, self.permissions & stat.S_ISUID)
+ ks += _rwx((self.permissions & 070) >> 3, self.permissions & stat.S_ISGID)
+ ks += _rwx(self.permissions & 7, self.permissions & stat.S_ISVTX, True)
+ else:
+ ks = '?---------'
+ uid = getattr(self, 'uid', -1)
+ gid = getattr(self, 'gid', -1)
+ size = getattr(self, 'size', -1)
+ mtime = getattr(self, 'mtime', 0)
+ # compute display date
+ if abs(time.time() - mtime) > 15552000:
+ # (15552000 = 6 months)
+ datestr = time.strftime('%d %b %Y', time.localtime(mtime))
+ else:
+ datestr = time.strftime('%d %b %H:%M', time.localtime(mtime))
+ return '%s 1 %-8d %-8d %8d %-12s' % (ks, uid, gid, size, datestr)
+
class SFTPError (Exception):
@@ -146,10 +235,9 @@ class SFTPFile (BufferedFile):
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
if t != CMD_ATTRS:
raise SFTPError('Expected attrs')
- attr = SFTPAttributes()
- attr.unpack(msg)
+ attr = SFTPAttributes._from_msg(msg)
try:
- return attr.size
+ return attr.st_size
except:
return 0
@@ -230,12 +318,17 @@ class SFTPFile (BufferedFile):
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
if t != CMD_ATTRS:
raise SFTPError('Expected attributes')
- attr = SFTPAttributes(msg)
- attr._pythonize()
- return attr
+ return SFTPAttributes._from_msg(msg)
class BaseSFTP (object):
+ def __init__(self):
+ self.logger = logging.getLogger('paramiko.sftp')
+
+
+ ### internals...
+
+
def _send_version(self):
self._send_packet(CMD_INIT, struct.pack('>I', _VERSION))
t, data = self._read_packet()
@@ -246,10 +339,14 @@ class BaseSFTP (object):
# raise SFTPError('Incompatible sftp protocol')
return version
-
- ### internals...
-
-
+ def _send_server_version(self):
+ self._send_packet(CMD_VERSION, struct.pack('>I', _VERSION))
+ 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 type(msg) == type([]):
for m in msg: