diff options
Diffstat (limited to 'paramiko/sftp_handle.py')
-rw-r--r-- | paramiko/sftp_handle.py | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/paramiko/sftp_handle.py b/paramiko/sftp_handle.py new file mode 100644 index 0000000..e1d93e9 --- /dev/null +++ b/paramiko/sftp_handle.py @@ -0,0 +1,188 @@ +# 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. + +""" +Abstraction of an SFTP file handle (for server mode). +""" + +import os + +from paramiko.common import * +from paramiko.sftp import * + + +class SFTPHandle (object): + """ + Abstract object representing a handle to an open file (or folder) in an + SFTP server implementation. Each handle has a string representation used + by the client to refer to the underlying file. + + Server implementations can (and should) subclass SFTPHandle to implement + features of a file handle, like L{stat} or L{chattr}. + """ + def __init__(self): + self.__name = None + # only for handles to folders: + self.__files = { } + self.__tell = None + + def close(self): + """ + When a client closes a file, this method is called on the handle. + Normally you would use this method to close the underlying OS level + file object(s). + + The default implementation checks for attributes on C{self} named + C{readfile} and/or C{writefile}, and if either or both are present, + their C{close()} methods are called. This means that if you are + using the default implementations of L{read} and L{write}, this + method's default implementation should be fine also. + """ + readfile = getattr(self, 'readfile', None) + if readfile is not None: + readfile.close() + writefile = getattr(self, 'writefile', None) + if writefile is not None: + writefile.close() + + def read(self, offset, length): + """ + Read up to C{length} bytes from this file, starting at position + C{offset}. The offset may be a python long, since SFTP allows it + to be 64 bits. + + If the end of the file has been reached, this method may return an + empty string to signify EOF, or it may also return L{SFTP_EOF}. + + The default implementation checks for an attribute on C{self} named + C{readfile}, and if present, performs the read operation on the python + file-like object found there. (This is meant as a time saver for the + common case where you are wrapping a python file object.) + + @param offset: position in the file to start reading from. + @type offset: int or long + @param length: number of bytes to attempt to read. + @type length: int + @return: data read from the file, or an SFTP error code. + @rtype: str + """ + if not hasattr(self, 'readfile') or (self.readfile is None): + return SFTP_OP_UNSUPPORTED + try: + if self.__tell is None: + self.__tell = self.readfile.tell() + if offset != self.__tell: + self.readfile.seek(offset) + self.__tell = offset + data = self.readfile.read(length) + except IOError, e: + self.__tell = None + return SFTPServer.convert_errno(e.errno) + self.__tell += len(data) + return data + + def write(self, offset, data): + """ + Write C{data} into this file at position C{offset}. Extending the + file past its original end is expected. Unlike python's normal + C{write()} methods, this method cannot do a partial write: it must + write all of C{data} or else return an error. + + The default implementation checks for an attribute on C{self} named + C{writefile}, and if present, performs the write operation on the + python file-like object found there. The attribute is named + differently from C{readfile} to make it easy to implement read-only + (or write-only) files, but if both attributes are present, they should + refer to the same file. + + @param offset: position in the file to start reading from. + @type offset: int or long + @param data: data to write into the file. + @type data: str + @return: an SFTP error code like L{SFTP_OK}. + """ + if not hasattr(self, 'writefile') or (self.writefile is None): + return SFTP_OP_UNSUPPORTED + try: + if self.__tell is None: + self.__tell = self.writefile.tell() + if offset != self.__tell: + self.writefile.seek(offset) + self.__tell = offset + self.writefile.write(data) + self.writefile.flush() + except IOError, e: + self.__tell = None + return SFTPServer.convert_errno(e.errno) + self.__tell += len(data) + return SFTP_OK + + def stat(self): + """ + Return an L{SFTPAttributes} object referring to this open file, or an + error code. This is equivalent to L{SFTPServerInterface.stat}, except + it's called on an open file instead of a path. + + @return: an attributes object for the given file, or an SFTP error + code (like L{SFTP_PERMISSION_DENIED}). + @rtype: L{SFTPAttributes} I{or error code} + """ + return SFTP_OP_UNSUPPORTED + + def chattr(self, attr): + """ + Change the attributes of this file. The C{attr} object will contain + only those fields provided by the client in its request, so you should + check for the presence of fields before using them. + + @param attr: the attributes to change on this file. + @type attr: L{SFTPAttributes} + @return: an error code like L{SFTP_OK}. + @rtype: int + """ + return SFTP_OP_UNSUPPORTED + + + ### internals... + + + def _set_files(self, files): + """ + Used by the SFTP server code to cache a directory listing. (In + the SFTP protocol, listing a directory is a multi-stage process + requiring a temporary handle.) + """ + self.__files = files + + def _get_next_files(self): + """ + Used by the SFTP server code to retreive a cached directory + listing. + """ + fnlist = self.__files[:16] + self.__files = self.__files[16:] + return fnlist + + def _get_name(self): + return self.__name + + def _set_name(self, name): + self.__name = name + + +from paramiko.sftp_server import SFTPServer |