Document FTP: rewrite the code, resolve nodes only once.
A big patch: nodes should not be calculated multiple times from the path string, within an operation. Instead, they are resolved only once in a node object (may reuse the cwd_node, if possible) and then op relative to that node. bzr revid: p_christ@hol.gr-20100702215249-kzucr3i1p5f6vvnt
This commit is contained in:
@@ -5,6 +5,7 @@ from tarfile import filemode
|
||||
import StringIO
|
||||
import base64
|
||||
import logging
|
||||
import errno
|
||||
|
||||
import glob
|
||||
import fnmatch
|
||||
@@ -70,11 +71,20 @@ class abstracted_fs(object):
|
||||
- (str) root: the user home directory.
|
||||
- (str) cwd: the current working directory.
|
||||
- (str) rnfr: source file to be renamed.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.root = None
|
||||
self.cwd = '/'
|
||||
self.cwd_node = None
|
||||
self.rnfr = None
|
||||
self._log = logging.getLogger('FTP.fs')
|
||||
|
||||
# Ok
|
||||
def db_list(self):
|
||||
#return pooler.pool_dic.keys()
|
||||
"""Get the list of available databases, with FTPd support
|
||||
"""
|
||||
s = netsvc.ExportService.getService('db')
|
||||
result = s.exp_list(document=True)
|
||||
self.db_name_list = []
|
||||
@@ -102,97 +112,58 @@ class abstracted_fs(object):
|
||||
# pooler.close_db(db_name)
|
||||
return self.db_name_list
|
||||
|
||||
# Ok
|
||||
def __init__(self):
|
||||
self.root = None
|
||||
self.cwd = '/'
|
||||
self.rnfr = None
|
||||
self._log = logging.getLogger('FTP.fs')
|
||||
|
||||
# --- Pathname / conversion utilities
|
||||
|
||||
# Ok
|
||||
def ftpnorm(self, ftppath):
|
||||
"""Normalize a "virtual" ftp pathname (tipically the raw string
|
||||
coming from client) depending on the current working directory.
|
||||
coming from client).
|
||||
|
||||
Example (having "/foo" as current working directory):
|
||||
'x' -> '/foo/x'
|
||||
|
||||
Note: directory separators are system independent ("/").
|
||||
Pathname returned is always absolutized.
|
||||
Pathname returned is relative!.
|
||||
"""
|
||||
if os.path.isabs(ftppath):
|
||||
p = os.path.normpath(ftppath)
|
||||
else:
|
||||
p = os.path.normpath(os.path.join(self.cwd, ftppath))
|
||||
p = os.path.normpath(ftppath)
|
||||
# normalize string in a standard web-path notation having '/'
|
||||
# as separator.
|
||||
# as separator. xrg: is that really in the spec?
|
||||
p = p.replace("\\", "/")
|
||||
# os.path.normpath supports UNC paths (e.g. "//a/b/c") but we
|
||||
# don't need them. In case we get an UNC path we collapse
|
||||
# redundant separators appearing at the beginning of the string
|
||||
while p[:2] == '//':
|
||||
p = p[1:]
|
||||
# Anti path traversal: don't trust user input, in the event
|
||||
# that self.cwd is not absolute, return "/" as a safety measure.
|
||||
# This is for extra protection, maybe not really necessary.
|
||||
if not os.path.isabs(p):
|
||||
p = "/"
|
||||
if p == '.':
|
||||
return ''
|
||||
return p
|
||||
|
||||
# Ok
|
||||
def ftp2fs(self, path_orig, data):
|
||||
path = self.ftpnorm(path_orig)
|
||||
if not data or (path and path=='/'):
|
||||
return None
|
||||
path2 = filter(None,path.split('/'))[1:]
|
||||
(cr, uid) = data
|
||||
if len(path2):
|
||||
path2[-1]=_to_unicode(path2[-1])
|
||||
|
||||
ctx = get_node_context(cr, uid, {})
|
||||
res = ctx.get_uri(cr, path2[:])
|
||||
if not res:
|
||||
raise OSError(2, 'Not such file or directory.')
|
||||
return res
|
||||
def get_cwd(self):
|
||||
""" return the cwd, decoded in utf"""
|
||||
return _to_decode(self.cwd)
|
||||
|
||||
def ftp2fs(self, path_orig, data):
|
||||
raise DeprecationWarning()
|
||||
|
||||
# Ok
|
||||
def fs2ftp(self, node):
|
||||
""" Return the string path of a node, in ftp form
|
||||
"""
|
||||
res='/'
|
||||
if node:
|
||||
paths = node.full_path()
|
||||
paths = map(lambda x: '/' +x, paths)
|
||||
res = os.path.normpath(''.join(paths))
|
||||
res = res.replace("\\", "/")
|
||||
while res[:2] == '//':
|
||||
res = res[1:]
|
||||
res = '/' + node.context.dbname + '/' + _to_decode(res)
|
||||
res = '/' + node.context.dbname + '/' + \
|
||||
_to_decode(os.path.join(*paths))
|
||||
|
||||
#res = node and ('/' + node.cr.dbname + '/' + _to_decode(self.ftpnorm(node.path))) or '/'
|
||||
return res
|
||||
|
||||
# Ok
|
||||
def validpath(self, path):
|
||||
"""Check whether the path belongs to user's home directory.
|
||||
Expected argument is a "real" filesystem pathname.
|
||||
|
||||
If path is a symbolic link it is resolved to check its real
|
||||
destination.
|
||||
|
||||
Pathnames escaping from user's root directory are considered
|
||||
not valid.
|
||||
Expected argument is a datacr tuple
|
||||
"""
|
||||
return path and True or False
|
||||
# TODO: are we called for "/" ?
|
||||
return isinstance(path, tuple) and path[1] and True or False
|
||||
|
||||
# --- Wrapper methods around open() and tempfile.mkstemp
|
||||
|
||||
def create(self, node, objname, mode):
|
||||
def create(self, datacr, objname, mode):
|
||||
""" Create a children file-node under node, open it
|
||||
@return open node_descriptor of the created node
|
||||
"""
|
||||
objname = _to_unicode(objname)
|
||||
cr = self.get_node_cr(node)
|
||||
cr , node, rem = datacr
|
||||
try:
|
||||
child = node.child(cr, objname)
|
||||
if child:
|
||||
@@ -200,10 +171,8 @@ class abstracted_fs(object):
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
|
||||
ret = child.open_data(cr, mode)
|
||||
cr.close()
|
||||
return ret
|
||||
except OSError:
|
||||
cr.close()
|
||||
except EnvironmentError:
|
||||
raise
|
||||
except Exception,e:
|
||||
self._log.exception('Cannot locate item %s at node %s', objname, repr(node))
|
||||
@@ -215,19 +184,13 @@ class abstracted_fs(object):
|
||||
except Exception,e:
|
||||
self._log.exception('Cannot create item %s at node %s', objname, repr(node))
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
finally:
|
||||
if cr:
|
||||
cr.close()
|
||||
|
||||
def open(self, node, mode):
|
||||
if not node:
|
||||
def open(self, datacr, mode):
|
||||
if not (datacr and datacr[1]):
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
# Reading operation
|
||||
cr = self.get_node_cr(node)
|
||||
try:
|
||||
res = node.open_data(cr, mode)
|
||||
finally:
|
||||
cr.close()
|
||||
cr, node, rem = datacr
|
||||
res = node.open_data(cr, mode)
|
||||
return res
|
||||
|
||||
# ok, but need test more
|
||||
@@ -258,26 +221,26 @@ class abstracted_fs(object):
|
||||
|
||||
|
||||
# Ok
|
||||
def chdir(self, path):
|
||||
if not path:
|
||||
def chdir(self, datacr):
|
||||
if (not datacr) or datacr == (None, None, None):
|
||||
self.cwd = '/'
|
||||
self.cwd_node = None
|
||||
return None
|
||||
if path.type in ('collection','database'):
|
||||
self.cwd = self.fs2ftp(path)
|
||||
elif path.type in ('file'):
|
||||
parent_path = path.full_path()[:-1]
|
||||
self.cwd = os.path.normpath(''.join(parent_path))
|
||||
else:
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
if not datacr[1]:
|
||||
raise OSError(1, 'Operation not permitted')
|
||||
if datacr[1].type not in ('collection','database'):
|
||||
raise OSError(2, 'Path is not a directory')
|
||||
self.cwd = '/'+datacr[1].context.dbname + '/'
|
||||
self.cwd += '/'.join(datacr[1].full_path())
|
||||
self.cwd_node = datacr[1]
|
||||
|
||||
# Ok
|
||||
def mkdir(self, node, basename):
|
||||
def mkdir(self, datacr, basename):
|
||||
"""Create the specified directory."""
|
||||
cr = False
|
||||
cr, node, rem = datacr or (None, None, None)
|
||||
if not node:
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
|
||||
cr = self.get_node_cr(node)
|
||||
try:
|
||||
basename =_to_unicode(basename)
|
||||
cdir = node.create_child_collection(cr, basename)
|
||||
@@ -290,31 +253,87 @@ class abstracted_fs(object):
|
||||
if cr: cr.close()
|
||||
|
||||
def close_cr(self, data):
|
||||
if data:
|
||||
if data and data[0]:
|
||||
data[0].close()
|
||||
return True
|
||||
|
||||
def get_cr(self, path):
|
||||
path = self.ftpnorm(path)
|
||||
if path=='/':
|
||||
return None
|
||||
dbname = path.split('/')[1]
|
||||
if dbname not in self.db_list():
|
||||
return None
|
||||
try:
|
||||
db = pooler.get_db(dbname)
|
||||
except Exception:
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
cr = db.cursor()
|
||||
try:
|
||||
uid = security.login(dbname, self.username, self.password)
|
||||
except Exception:
|
||||
cr.close()
|
||||
raise
|
||||
if not uid:
|
||||
cr.close()
|
||||
raise OSError(2, 'Authentification Required.')
|
||||
return cr, uid
|
||||
def get_cr(self, pathname):
|
||||
raise DeprecationWarning()
|
||||
|
||||
def get_crdata(self, line, mode='file'):
|
||||
""" Get database cursor, node and remainder data, for commands
|
||||
|
||||
This is the helper function that will prepare the arguments for
|
||||
any of the subsequent commands.
|
||||
It returns a tuple in the form of:
|
||||
@code ( cr, node, rem_path=None )
|
||||
|
||||
@param line An absolute or relative ftp path, as passed to the cmd.
|
||||
@param mode A word describing the mode of operation, so that this
|
||||
function behaves properly in the different commands.
|
||||
"""
|
||||
path = self.ftpnorm(line)
|
||||
if self.cwd_node is None:
|
||||
if not os.path.isabs(path):
|
||||
path = os.path.join(self.root, path)
|
||||
|
||||
if path == '/' and mode in ('list', 'cwd'):
|
||||
return (None, None, None )
|
||||
|
||||
path = os.path.normpath(path) # again, for '/db/../ss'
|
||||
if path == '.': path = ''
|
||||
|
||||
if os.path.isabs(path) and self.cwd_node is not None \
|
||||
and path.startswith(self.cwd):
|
||||
# make relative, so that cwd_node is used again
|
||||
path = path[len(self.cwd):]
|
||||
if path.startswith('/'):
|
||||
path = path[1:]
|
||||
|
||||
p_parts = path.split('/') # hard-code the unix sep here, by spec.
|
||||
|
||||
assert '..' not in p_parts
|
||||
|
||||
rem_path = None
|
||||
if mode in ('create',):
|
||||
rem_path = p_parts[-1]
|
||||
p_parts = p_parts[:-1]
|
||||
|
||||
if os.path.isabs(path):
|
||||
# we have to start from root, again
|
||||
p_parts = p_parts[1:]
|
||||
dbname = p_parts[0]
|
||||
if dbname not in self.db_list():
|
||||
return None
|
||||
try:
|
||||
db = pooler.get_db(dbname)
|
||||
except Exception:
|
||||
raise OSError(1, 'Database cannot be used.')
|
||||
cr = db.cursor()
|
||||
try:
|
||||
uid = security.login(dbname, self.username, self.password)
|
||||
except Exception:
|
||||
cr.close()
|
||||
raise
|
||||
if not uid:
|
||||
cr.close()
|
||||
raise OSError(2, 'Authentification Required.')
|
||||
n = get_node_context(cr, uid, {})
|
||||
node = n.get_uri(cr, p_parts[1:])
|
||||
return (cr, node, rem_path)
|
||||
else:
|
||||
# we never reach here if cwd_node is not set
|
||||
if p_parts and p_parts[-1] == '':
|
||||
p_parts = p_parts[:-1]
|
||||
cr, uid = self.get_node_cr_uid(self.cwd_node)
|
||||
if p_parts:
|
||||
node = self.cwd_node.get_uri(cr, p_parts)
|
||||
else:
|
||||
node = self.cwd_node
|
||||
if node is False and mode not in ('???'):
|
||||
cr.close()
|
||||
raise IOError(errno.ENOENT, 'Path does not exist')
|
||||
return (cr, node, rem_path)
|
||||
|
||||
def get_node_cr_uid(self, node):
|
||||
""" Get cr, uid, pool from a node
|
||||
@@ -331,16 +350,21 @@ class abstracted_fs(object):
|
||||
"""
|
||||
return self.get_node_cr_uid(node)[0]
|
||||
|
||||
def listdir(self, path):
|
||||
def listdir(self, datacr):
|
||||
"""List the content of a directory."""
|
||||
class false_node(object):
|
||||
write_date = None
|
||||
create_date = None
|
||||
write_date = 0.0
|
||||
create_date = 0.0
|
||||
unixperms = 040550
|
||||
content_length = 0L
|
||||
uuser = 'root'
|
||||
ugroup = 'root'
|
||||
type = 'database'
|
||||
|
||||
def __init__(self, db):
|
||||
self.path = '/'+db
|
||||
self.path = db
|
||||
|
||||
if path is None:
|
||||
if datacr[1] is None:
|
||||
result = []
|
||||
for db in self.db_list():
|
||||
try:
|
||||
@@ -348,76 +372,57 @@ class abstracted_fs(object):
|
||||
except osv.except_osv:
|
||||
pass
|
||||
return result
|
||||
cr = self.get_node_cr(path)
|
||||
res = path.children(cr)
|
||||
cr.close()
|
||||
cr, node, rem = datacr
|
||||
res = node.children(cr)
|
||||
return res
|
||||
|
||||
def rmdir(self, node):
|
||||
def rmdir(self, datacr):
|
||||
"""Remove the specified directory."""
|
||||
cr, node, rem = datacr
|
||||
assert node
|
||||
cr = self.get_node_cr(node)
|
||||
try:
|
||||
node.rmcol(cr)
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
node.rmcol(cr)
|
||||
cr.commit()
|
||||
|
||||
def remove(self, node):
|
||||
assert node
|
||||
if node.type == 'collection':
|
||||
return self.rmdir(node)
|
||||
elif node.type == 'file':
|
||||
return self.rmfile(node)
|
||||
def remove(self, datacr):
|
||||
assert datacr[1]
|
||||
if datacr[1].type == 'collection':
|
||||
return self.rmdir(datacr)
|
||||
elif datacr[1].type == 'file':
|
||||
return self.rmfile(datacr)
|
||||
raise OSError(1, 'Operation not permited.')
|
||||
|
||||
def rmfile(self, node):
|
||||
def rmfile(self, datacr):
|
||||
"""Remove the specified file."""
|
||||
assert node
|
||||
cr = self.get_node_cr(node)
|
||||
try:
|
||||
node.rm(cr)
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
assert datacr[1]
|
||||
cr = datacr[0]
|
||||
datacr[1].rm(cr)
|
||||
cr.commit()
|
||||
|
||||
def rename(self, src, dst_basedir, dst_basename):
|
||||
def rename(self, src, datacr):
|
||||
""" Renaming operation, the effect depends on the src:
|
||||
* A file: read, create and remove
|
||||
* A directory: change the parent and reassign childs to ressource
|
||||
"""
|
||||
cr = self.get_node_cr(src)
|
||||
cr = datacr[0]
|
||||
try:
|
||||
nname = _to_unicode(dst_basename)
|
||||
ret = src.move_to(cr, dst_basedir, new_name=nname)
|
||||
nname = _to_unicode(datacr[2])
|
||||
ret = src.move_to(cr, datacr[1], new_name=nname)
|
||||
# API shouldn't wait for us to write the object
|
||||
assert (ret is True) or (ret is False)
|
||||
cr.commit()
|
||||
except Exception,err:
|
||||
self._log.exception('Cannot rename "%s" to "%s" at "%s"', src, dst_basename, dst_basedir)
|
||||
raise OSError(1,'Operation not permited.')
|
||||
finally:
|
||||
if cr: cr.close()
|
||||
|
||||
|
||||
|
||||
# Nearly Ok
|
||||
def stat(self, node):
|
||||
r = list(os.stat('/'))
|
||||
if self.isfile(node):
|
||||
r[0] = 33188
|
||||
r[6] = self.getsize(node)
|
||||
r[7] = self.getmtime(node)
|
||||
r[8] = self.getmtime(node)
|
||||
r[9] = self.getmtime(node)
|
||||
return os.stat_result(r)
|
||||
lstat = stat
|
||||
raise NotImplementedError()
|
||||
|
||||
# --- Wrapper methods around os.path.*
|
||||
|
||||
# Ok
|
||||
def isfile(self, node):
|
||||
if node and (node.type not in ('collection','database')):
|
||||
if node and (node.type in ('file','content')):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -426,7 +431,6 @@ class abstracted_fs(object):
|
||||
"""Return True if path is a symbolic link."""
|
||||
return False
|
||||
|
||||
# Ok
|
||||
def isdir(self, node):
|
||||
"""Return True if path is a directory."""
|
||||
if node is None:
|
||||
@@ -435,19 +439,20 @@ class abstracted_fs(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Ok
|
||||
def getsize(self, node):
|
||||
def getsize(self, datacr):
|
||||
"""Return the size of the specified file in bytes."""
|
||||
result = 0L
|
||||
if node.type=='file':
|
||||
result = node.content_length or 0L
|
||||
return result
|
||||
if not (datacr and datacr[1]):
|
||||
return 0L
|
||||
if datacr[1].type in ('file', 'content'):
|
||||
return datacr[1].content_length or 0L
|
||||
return 0L
|
||||
|
||||
# Ok
|
||||
def getmtime(self, node):
|
||||
def getmtime(self, datacr):
|
||||
"""Return the last modified time as a number of seconds since
|
||||
the epoch."""
|
||||
|
||||
node = datacr[1]
|
||||
if node.write_date or node.create_date:
|
||||
dt = (node.write_date or node.create_date)[:19]
|
||||
result = time.mktime(time.strptime(dt, '%Y-%m-%d %H:%M:%S'))
|
||||
@@ -468,7 +473,9 @@ class abstracted_fs(object):
|
||||
"""Return True if path refers to an existing path, including
|
||||
a broken or circular symbolic link.
|
||||
"""
|
||||
raise DeprecationWarning()
|
||||
return path and True or False
|
||||
|
||||
exists = lexists
|
||||
|
||||
# Ok, can be improved
|
||||
@@ -487,23 +494,21 @@ class abstracted_fs(object):
|
||||
|
||||
# note: the following operations are no more blocking
|
||||
|
||||
# Ok
|
||||
def get_list_dir(self, path):
|
||||
def get_list_dir(self, datacr):
|
||||
""""Return an iterator object that yields a directory listing
|
||||
in a form suitable for LIST command.
|
||||
"""
|
||||
if self.isdir(path):
|
||||
listing = self.listdir(path)
|
||||
if not datacr:
|
||||
return None
|
||||
elif self.isdir(datacr[1]):
|
||||
listing = self.listdir(datacr)
|
||||
#listing.sort()
|
||||
return self.format_list(path and path.path or '/', listing)
|
||||
return self.format_list(datacr[0], datacr[1], listing)
|
||||
# if path is a file or a symlink we return information about it
|
||||
elif self.isfile(path):
|
||||
basedir, filename = os.path.split(path.path)
|
||||
self.lstat(path) # raise exc in case of problems
|
||||
return self.format_list(basedir, [path])
|
||||
elif self.isfile(datacr[1]):
|
||||
par = datacr[1].parent
|
||||
return self.format_list(datacr[0], par, [datacr[1]])
|
||||
|
||||
|
||||
# Ok
|
||||
def get_stat_dir(self, rawline, datacr):
|
||||
"""Return an iterator object that yields a list of files
|
||||
matching a dirname pattern non-recursively in a form
|
||||
@@ -526,13 +531,12 @@ class abstracted_fs(object):
|
||||
listing.sort()
|
||||
return self.format_list(basedir, listing)
|
||||
|
||||
# Ok
|
||||
def format_list(self, basedir, listing, ignore_err=True):
|
||||
def format_list(self, cr, parent_node, listing, ignore_err=True):
|
||||
"""Return an iterator object that yields the entries of given
|
||||
directory emulating the "/bin/ls -lA" UNIX command output.
|
||||
|
||||
- (str) basedir: the absolute dirname.
|
||||
- (list) listing: the names of the entries in basedir
|
||||
- (str) basedir: the parent directory node. Can be None
|
||||
- (list) listing: a list of nodes
|
||||
- (bool) ignore_err: when False raise exception if os.lstat()
|
||||
call fails.
|
||||
|
||||
@@ -548,35 +552,35 @@ class abstracted_fs(object):
|
||||
drwxrwxrwx 1 owner group 0 Aug 31 18:50 e-books
|
||||
-rw-rw-rw- 1 owner group 380 Sep 02 3:40 module.py
|
||||
"""
|
||||
for file in listing:
|
||||
try:
|
||||
st = self.lstat(file)
|
||||
except os.error:
|
||||
if ignore_err:
|
||||
continue
|
||||
raise
|
||||
perms = filemode(st.st_mode) # permissions
|
||||
nlinks = st.st_nlink # number of links to inode
|
||||
if not nlinks: # non-posix system, let's use a bogus value
|
||||
nlinks = 1
|
||||
size = st.st_size # file size
|
||||
uname = "owner"
|
||||
gname = "group"
|
||||
for node in listing:
|
||||
perms = filemode(node.unixperms) # permissions
|
||||
nlinks = 1
|
||||
size = node.content_length or 0L
|
||||
uname = node.uuser
|
||||
gname = node.ugroup
|
||||
# stat.st_mtime could fail (-1) if last mtime is too old
|
||||
# in which case we return the local time as last mtime
|
||||
try:
|
||||
mname=_get_month_name(time.strftime("%m", time.localtime(st.st_mtime)))
|
||||
mtime = mname+' '+time.strftime("%d %H:%M", time.localtime(st.st_mtime))
|
||||
st_mtime = node.write_date or 0.0
|
||||
if isinstance(st_mtime, basestring):
|
||||
st_mtime = time.strptime(st_mtime, '%Y-%m-%d %H:%M:%S')
|
||||
elif isinstance(st_mtime, float):
|
||||
st_mtime = time.localtime(st_mtime)
|
||||
mname=_get_month_name(time.strftime("%m", st_mtime ))
|
||||
mtime = mname+' '+time.strftime("%d %H:%M", st_mtime)
|
||||
except ValueError:
|
||||
mname=_get_month_name(time.strftime("%m"))
|
||||
mtime = mname+' '+time.strftime("%d %H:%M")
|
||||
fpath = node.path
|
||||
if isinstance(fpath, (list, tuple)):
|
||||
fpath = fpath[-1]
|
||||
# formatting is matched with proftpd ls output
|
||||
path=_to_decode(file.path) #file.path.encode('ascii','replace').replace('?','_')
|
||||
path=_to_decode(fpath)
|
||||
yield "%s %3s %-8s %-8s %8s %s %s\r\n" %(perms, nlinks, uname, gname,
|
||||
size, mtime, path.split('/')[-1])
|
||||
size, mtime, path)
|
||||
|
||||
# Ok
|
||||
def format_mlsx(self, basedir, listing, perms, facts, ignore_err=True):
|
||||
def format_mlsx(self, cr, basedir, listing, perms, facts, ignore_err=True):
|
||||
"""Return an iterator object that yields the entries of a given
|
||||
directory or of a single file in a form suitable with MLSD and
|
||||
MLST commands.
|
||||
@@ -609,15 +613,9 @@ class abstracted_fs(object):
|
||||
if 'd' in perms:
|
||||
permdir += 'p'
|
||||
type = size = perm = modify = create = unique = mode = uid = gid = ""
|
||||
for file in listing:
|
||||
try:
|
||||
st = self.stat(file)
|
||||
except OSError:
|
||||
if ignore_err:
|
||||
continue
|
||||
raise
|
||||
for node in listing:
|
||||
# type + perm
|
||||
if stat.S_ISDIR(st.st_mode):
|
||||
if self.isdir(node):
|
||||
if 'type' in facts:
|
||||
type = 'type=dir;'
|
||||
if 'perm' in facts:
|
||||
@@ -628,29 +626,37 @@ class abstracted_fs(object):
|
||||
if 'perm' in facts:
|
||||
perm = 'perm=%s;' %permfile
|
||||
if 'size' in facts:
|
||||
size = 'size=%s;' %st.st_size # file size
|
||||
size = 'size=%s;' % (node.content_length or 0L)
|
||||
# last modification time
|
||||
if 'modify' in facts:
|
||||
try:
|
||||
modify = 'modify=%s;' %time.strftime("%Y%m%d%H%M%S",
|
||||
time.localtime(st.st_mtime))
|
||||
st_mtime = node.write_date or 0.0
|
||||
if isinstance(st_mtime, basestring):
|
||||
st_mtime = time.strptime(st_mtime, '%Y-%m-%d %H:%M:%S')
|
||||
elif isinstance(st_mtime, float):
|
||||
st_mtime = time.localtime(st_mtime)
|
||||
modify = 'modify=%s;' %time.strftime("%Y%m%d%H%M%S", st_mtime)
|
||||
except ValueError:
|
||||
# stat.st_mtime could fail (-1) if last mtime is too old
|
||||
modify = ""
|
||||
if 'create' in facts:
|
||||
# on Windows we can provide also the creation time
|
||||
try:
|
||||
create = 'create=%s;' %time.strftime("%Y%m%d%H%M%S",
|
||||
time.localtime(st.st_ctime))
|
||||
st_ctime = node.create_date or 0.0
|
||||
if isinstance(st_ctime, basestring):
|
||||
st_ctime = time.strptime(st_ctime, '%Y-%m-%d %H:%M:%S')
|
||||
elif isinstance(st_mtime, float):
|
||||
st_ctime = time.localtime(st_ctime)
|
||||
create = 'create=%s;' %time.strftime("%Y%m%d%H%M%S",st_ctime)
|
||||
except ValueError:
|
||||
create = ""
|
||||
# UNIX only
|
||||
if 'unix.mode' in facts:
|
||||
mode = 'unix.mode=%s;' %oct(st.st_mode & 0777)
|
||||
mode = 'unix.mode=%s;' %oct(node.unixperms & 0777)
|
||||
if 'unix.uid' in facts:
|
||||
uid = 'unix.uid=%s;' %st.st_uid
|
||||
uid = 'unix.uid=%s;' % node.uuser
|
||||
if 'unix.gid' in facts:
|
||||
gid = 'unix.gid=%s;' %st.st_gid
|
||||
gid = 'unix.gid=%s;' % node.ugroup
|
||||
# We provide unique fact (see RFC-3659, chapter 7.5.2) on
|
||||
# posix platforms only; we get it by mixing st_dev and
|
||||
# st_ino values which should be enough for granting an
|
||||
@@ -659,10 +665,12 @@ class abstracted_fs(object):
|
||||
# Implementors who want to provide unique fact on other
|
||||
# platforms should use some platform-specific method (e.g.
|
||||
# on Windows NTFS filesystems MTF records could be used).
|
||||
if 'unique' in facts:
|
||||
unique = "unique=%x%x;" %(st.st_dev, st.st_ino)
|
||||
path=_to_decode(file.path)
|
||||
path = path and path.split('/')[-1] or None
|
||||
# if 'unique' in facts: todo
|
||||
# unique = "unique=%x%x;" %(st.st_dev, st.st_ino)
|
||||
path = node.path
|
||||
if isinstance (path, (list, tuple)):
|
||||
path = path[-1]
|
||||
path=_to_decode(path)
|
||||
yield "%s%s%s%s%s%s%s%s%s %s\r\n" %(type, size, perm, modify, create,
|
||||
mode, uid, gid, unique, path)
|
||||
|
||||
|
||||
@@ -1138,19 +1138,11 @@ class AbstractedFS:
|
||||
|
||||
# note: the following operations are no more blocking
|
||||
|
||||
def get_list_dir(self, path):
|
||||
def get_list_dir(self, datacr):
|
||||
""""Return an iterator object that yields a directory listing
|
||||
in a form suitable for LIST command.
|
||||
"""
|
||||
if self.isdir(path):
|
||||
listing = self.listdir(path)
|
||||
listing.sort()
|
||||
return self.format_list(path, listing)
|
||||
# if path is a file or a symlink we return information about it
|
||||
else:
|
||||
basedir, filename = os.path.split(path)
|
||||
self.lstat(path) # raise exc in case of problems
|
||||
return self.format_list(basedir, [filename])
|
||||
raise DeprecationWarning()
|
||||
|
||||
def get_stat_dir(self, rawline):
|
||||
"""Return an iterator object that yields a list of files
|
||||
@@ -1591,26 +1583,8 @@ class FTPHandler(asynchat.async_chat):
|
||||
|
||||
def __check_path(self, cmd, line):
|
||||
"""Check whether a path is valid."""
|
||||
# For the following commands we have to make sure that the real
|
||||
# path destination belongs to the user's root directory.
|
||||
# If provided path is a symlink we follow its final destination
|
||||
# to do so.
|
||||
if cmd in ('APPE','CWD','DELE','MDTM','NLST','MLSD','MLST','RETR',
|
||||
'RMD','SIZE','STOR','XCWD','XRMD'):
|
||||
datacr = None
|
||||
datacr = self.fs.get_cr(line)
|
||||
try:
|
||||
if not self.fs.validpath(self.fs.ftp2fs(line, datacr)):
|
||||
line = self.fs.ftpnorm(line)
|
||||
err = '"%s" points to a path which is outside ' \
|
||||
"the user's root directory" %line
|
||||
self.respond("550 %s." %err)
|
||||
self.log('FAIL %s "%s". %s.' %(cmd, line, err))
|
||||
self.fs.close_cr(datacr)
|
||||
return False
|
||||
except:
|
||||
pass
|
||||
self.fs.close_cr(datacr)
|
||||
|
||||
# Always true, we will only check later, once we have a cursor
|
||||
return True
|
||||
|
||||
def __check_perm(self, cmd, line, datacr):
|
||||
@@ -1623,6 +1597,7 @@ class FTPHandler(asynchat.async_chat):
|
||||
'RNFR':'f',
|
||||
'MKD':'m', 'XMKD':'m',
|
||||
'STOR':'w'}
|
||||
raise NotImplementedError
|
||||
if cmd in map:
|
||||
if cmd == 'STAT' and not line:
|
||||
return True
|
||||
@@ -2032,56 +2007,65 @@ class FTPHandler(asynchat.async_chat):
|
||||
# - Some older FTP clients erroneously issue /bin/ls-like LIST
|
||||
# formats in which case we fall back on cwd as default.
|
||||
if not line or line.lower() in ('-a', '-l', '-al', '-la'):
|
||||
line = self.fs.cwd
|
||||
line = ''
|
||||
datacr = None
|
||||
try:
|
||||
data = None
|
||||
data = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, data)
|
||||
line = self.fs.ftpnorm(line)
|
||||
iterator = self.run_as_current_user(self.fs.get_list_dir, path)
|
||||
datacr = self.fs.get_crdata(line, mode='list')
|
||||
iterator = self.run_as_current_user(self.fs.get_list_dir, datacr)
|
||||
except IOError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
self.respond('550 %s.'% err.strerror)
|
||||
return
|
||||
except OSError, err:
|
||||
self.fs.close_cr(data)
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
self.log('FAIL LIST "%s". %s.' %(line, why))
|
||||
self.respond('550 %s.' %why)
|
||||
else:
|
||||
self.fs.close_cr(data)
|
||||
self.log('OK LIST "%s". Transfer starting.' %line)
|
||||
return
|
||||
try:
|
||||
self.log('OK LIST "%s". Transfer starting.' % line)
|
||||
producer = BufferedIteratorProducer(iterator)
|
||||
self.push_dtp_data(producer, isproducer=True)
|
||||
finally:
|
||||
self.fs.close_cr(datacr)
|
||||
|
||||
|
||||
def ftp_NLST(self, line):
|
||||
"""Return a list of files in the specified directory in a
|
||||
compact form to the client.
|
||||
"""
|
||||
if not line:
|
||||
line = self.fs.cwd
|
||||
line = ''
|
||||
|
||||
datacr = None
|
||||
try:
|
||||
data = None
|
||||
data = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, data)
|
||||
line = self.fs.ftpnorm(line)
|
||||
if self.fs.isdir(path):
|
||||
listing = self.run_as_current_user(self.fs.listdir, path)
|
||||
listing = map(lambda x:os.path.split(x.path)[1], listing)
|
||||
datacr = self.fs.get_crdata(line, mode='list')
|
||||
if not datacr:
|
||||
datacr = ( None, None, None )
|
||||
if self.fs.isdir(datacr[1]):
|
||||
listing = self.run_as_current_user(self.fs.listdir, datacr)
|
||||
listing = [ x.path[-1] for x in listing ]
|
||||
else:
|
||||
# if path is a file we just list its name
|
||||
self.fs.lstat(path) # raise exc in case of problems
|
||||
basedir, filename = os.path.split(line)
|
||||
listing = [filename]
|
||||
listing = [datacr[1].path[-1],]
|
||||
except IOError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
self.respond('550 %s.'% err.strerror)
|
||||
return
|
||||
except OSError, err:
|
||||
self.fs.close_cr(data)
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
self.log('FAIL NLST "%s". %s.' %(line, why))
|
||||
self.respond('550 %s.' %why)
|
||||
else:
|
||||
self.fs.close_cr(data)
|
||||
data = ''
|
||||
if listing:
|
||||
listing.sort()
|
||||
data = '\r\n'.join(listing) + '\r\n'
|
||||
self.log('OK NLST "%s". Transfer starting.' %line)
|
||||
self.push_dtp_data(data)
|
||||
return
|
||||
|
||||
self.fs.close_cr(datacr)
|
||||
data = ''
|
||||
if listing:
|
||||
listing.sort()
|
||||
data = '\r\n'.join(listing) + '\r\n'
|
||||
self.log('OK NLST "%s". Transfer starting.' %line)
|
||||
self.push_dtp_data(data)
|
||||
|
||||
# --- MLST and MLSD commands
|
||||
|
||||
@@ -2096,18 +2080,15 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""
|
||||
# if no argument, fall back on cwd as default
|
||||
if not line:
|
||||
line = self.fs.cwd
|
||||
line = ''
|
||||
datacr = None
|
||||
try:
|
||||
datacr = None
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
basedir, basename = os.path.split(path)
|
||||
datacr = self.fs.get_crdata(line, mode='list')
|
||||
perms = self.authorizer.get_perms(self.username)
|
||||
iterator = self.run_as_current_user(self.fs.format_mlsx, basedir,
|
||||
[basename], perms, self.current_facts, ignore_err=False)
|
||||
iterator = self.run_as_current_user(self.fs.format_mlsx, datacr[0], datacr[1].parent,
|
||||
[datacr[1],], perms, self.current_facts, ignore_err=False)
|
||||
data = ''.join(iterator)
|
||||
except OSError, err:
|
||||
except EnvironmentError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
self.log('FAIL MLST "%s". %s.' %(line, why))
|
||||
@@ -2129,19 +2110,18 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""
|
||||
# if no argument, fall back on cwd as default
|
||||
if not line:
|
||||
line = self.fs.cwd
|
||||
line = ''
|
||||
|
||||
datacr = None
|
||||
try:
|
||||
datacr = None
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
datacr = self.fs.get_crdata(line, mode='list')
|
||||
# RFC-3659 requires 501 response code if path is not a directory
|
||||
if not self.fs.isdir(path):
|
||||
if not self.fs.isdir(datacr[1]):
|
||||
err = 'No such directory'
|
||||
self.log('FAIL MLSD "%s". %s.' %(line, err))
|
||||
self.respond("501 %s." %err)
|
||||
return
|
||||
listing = self.run_as_current_user(self.fs.listdir, path)
|
||||
listing = self.run_as_current_user(self.fs.listdir, datacr)
|
||||
except OSError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
@@ -2150,7 +2130,7 @@ class FTPHandler(asynchat.async_chat):
|
||||
else:
|
||||
self.fs.close_cr(datacr)
|
||||
perms = self.authorizer.get_perms(self.username)
|
||||
iterator = self.fs.format_mlsx(path, listing, perms,
|
||||
iterator = self.fs.format_mlsx(datacr[0], datacr[1], listing, perms,
|
||||
self.current_facts)
|
||||
producer = BufferedIteratorProducer(iterator)
|
||||
self.log('OK MLSD "%s". Transfer starting.' %line)
|
||||
@@ -2160,12 +2140,10 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""Retrieve the specified file (transfer from the server to the
|
||||
client)
|
||||
"""
|
||||
datacr = None
|
||||
try:
|
||||
datacr = None
|
||||
datacr = self.fs.get_cr(line)
|
||||
file = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
fd = self.run_as_current_user(self.fs.open, file, 'rb')
|
||||
datacr = self.fs.get_crdata(line, mode='file')
|
||||
fd = self.run_as_current_user(self.fs.open, datacr, 'rb')
|
||||
except OSError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
@@ -2174,7 +2152,7 @@ class FTPHandler(asynchat.async_chat):
|
||||
return
|
||||
except IOError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
why = err.strerror
|
||||
self.log('FAIL RETR "%s". %s.' %(line, why))
|
||||
self.respond('550 %s.' %why)
|
||||
return
|
||||
@@ -2187,7 +2165,7 @@ class FTPHandler(asynchat.async_chat):
|
||||
# the REST.
|
||||
ok = 0
|
||||
try:
|
||||
assert not self.restart_position > self.fs.getsize(file)
|
||||
assert not self.restart_position > self.fs.getsize(datacr)
|
||||
fd.seek(self.restart_position)
|
||||
ok = 1
|
||||
except AssertionError:
|
||||
@@ -2217,14 +2195,9 @@ class FTPHandler(asynchat.async_chat):
|
||||
else:
|
||||
cmd = 'STOR'
|
||||
|
||||
line = self.fs.ftpnorm(line)
|
||||
basedir,basename = os.path.split(line)
|
||||
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
file = self.fs.ftp2fs(basedir, datacr)
|
||||
|
||||
datacr = self.fs.get_crdata(line,mode='create')
|
||||
except OSError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
@@ -2235,7 +2208,7 @@ class FTPHandler(asynchat.async_chat):
|
||||
if self.restart_position:
|
||||
mode = 'r+'
|
||||
try:
|
||||
fd = self.run_as_current_user(self.fs.create, file, basename, mode + 'b')
|
||||
fd = self.run_as_current_user(self.fs.create, datacr, datacr[2], mode + 'b')
|
||||
except IOError, err:
|
||||
self.fs.close_cr(datacr)
|
||||
why = _strerror(err)
|
||||
@@ -2251,7 +2224,7 @@ class FTPHandler(asynchat.async_chat):
|
||||
# specified in the REST.
|
||||
ok = 0
|
||||
try:
|
||||
assert not self.restart_position > self.fs.getsize(self.fs.ftp2fs(line, datacr))
|
||||
assert not self.restart_position > self.fs.getsize(datacr)
|
||||
fd.seek(self.restart_position)
|
||||
ok = 1
|
||||
except AssertionError:
|
||||
@@ -2293,15 +2266,12 @@ class FTPHandler(asynchat.async_chat):
|
||||
self.respond("450 Can't STOU while REST request is pending.")
|
||||
return
|
||||
|
||||
datacr = None
|
||||
datacr = self.fs.get_cr(line)
|
||||
|
||||
if line:
|
||||
line = self.fs.ftpnorm(line)
|
||||
basedir,prefix = os.path.split(line)
|
||||
basedir = self.fs.ftp2fs(basedir, datacr)
|
||||
#prefix = prefix + '.'
|
||||
datacr = self.fs.get_crdata(line, mode='create')
|
||||
# TODO
|
||||
else:
|
||||
# TODO
|
||||
basedir = self.fs.ftp2fs(self.fs.cwd, datacr)
|
||||
prefix = 'ftpd.'
|
||||
try:
|
||||
@@ -2500,21 +2470,23 @@ class FTPHandler(asynchat.async_chat):
|
||||
|
||||
def ftp_PWD(self, line):
|
||||
"""Return the name of the current working directory to the client."""
|
||||
self.respond('257 "%s" is the current directory.' %self.fs.cwd)
|
||||
cwd = self.fs.get_cwd()
|
||||
self.respond('257 "%s" is the current directory.' % cwd)
|
||||
|
||||
def ftp_CWD(self, line):
|
||||
"""Change the current working directory."""
|
||||
# TODO: a lot of FTP servers go back to root directory if no
|
||||
# check: a lot of FTP servers go back to root directory if no
|
||||
# arg is provided but this is not specified in RFC-959.
|
||||
# Search for official references about this behaviour.
|
||||
if not line:
|
||||
line = '/'
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
self.run_as_current_user(self.fs.chdir, path)
|
||||
except OSError, err:
|
||||
datacr = self.fs.get_crdata(line,'cwd')
|
||||
self.run_as_current_user(self.fs.chdir, datacr)
|
||||
cwd = self.fs.get_cwd()
|
||||
self.log('OK CWD "%s".' % cwd)
|
||||
self.respond('250 "%s" is the current directory.' % cwd)
|
||||
except EnvironmentError, err:
|
||||
self.log("Could not cwd: %s" % err)
|
||||
if err.errno==2:
|
||||
why = 'Authentication Required or Failed'
|
||||
self.log('FAIL CWD "%s". %s.' %(self.fs.ftpnorm(line), why))
|
||||
@@ -2523,9 +2495,6 @@ class FTPHandler(asynchat.async_chat):
|
||||
why = _strerror(err)
|
||||
self.log('FAIL CWD "%s". %s.' %(self.fs.ftpnorm(line), why))
|
||||
self.respond('550 %s.' %why)
|
||||
else:
|
||||
self.log('OK CWD "%s".' %self.fs.cwd)
|
||||
self.respond('250 "%s" is the current directory.' %self.fs.cwd)
|
||||
self.fs.close_cr(datacr)
|
||||
|
||||
def ftp_CDUP(self, line):
|
||||
@@ -2554,17 +2523,15 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
if self.fs.isdir(path):
|
||||
why = "%s is not retrievable" %line
|
||||
self.log('FAIL SIZE "%s". %s.' %(line, why))
|
||||
self.respond("550 %s." %why)
|
||||
self.fs.close_cr(datacr)
|
||||
return
|
||||
size = self.run_as_current_user(self.fs.getsize, path)
|
||||
except OSError, err:
|
||||
datacr = self.fs.get_crdata(line, mode='file')
|
||||
#if self.fs.isdir(datacr[1]):
|
||||
# why = "%s is not retrievable" %line
|
||||
# self.log('FAIL SIZE "%s". %s.' %(line, why))
|
||||
# self.respond("550 %s." %why)
|
||||
# self.fs.close_cr(datacr)
|
||||
# return
|
||||
size = self.run_as_current_user(self.fs.getsize, datacr)
|
||||
except EnvironmentError, err:
|
||||
why = _strerror(err)
|
||||
self.log('FAIL SIZE "%s". %s.' %(line, why))
|
||||
self.respond('550 %s.' %why)
|
||||
@@ -2579,35 +2546,31 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
if not self.fs.isfile(self.fs.realpath(path)):
|
||||
datacr = self.fs.get_crdata(line)
|
||||
if not self.fs.isfile(datacr):
|
||||
why = "%s is not retrievable" %line
|
||||
self.log('FAIL MDTM "%s". %s.' %(line, why))
|
||||
self.respond("550 %s." %why)
|
||||
self.fs.close_cr(datacr)
|
||||
return
|
||||
lmt = self.run_as_current_user(self.fs.getmtime, path)
|
||||
lmt = self.run_as_current_user(self.fs.getmtime, datacr)
|
||||
lmt = time.strftime("%Y%m%d%H%M%S", time.localtime(lmt))
|
||||
self.respond("213 %s" %lmt)
|
||||
self.log('OK MDTM "%s".' %line)
|
||||
except OSError, err:
|
||||
why = _strerror(err)
|
||||
self.log('FAIL MDTM "%s". %s.' %(line, why))
|
||||
self.respond('550 %s.' %why)
|
||||
else:
|
||||
lmt = time.strftime("%Y%m%d%H%M%S", time.localtime(lmt))
|
||||
self.respond("213 %s" %lmt)
|
||||
self.log('OK MDTM "%s".' %line)
|
||||
self.fs.close_cr(datacr)
|
||||
|
||||
def ftp_MKD(self, line):
|
||||
"""Create the specified directory."""
|
||||
datacr = None
|
||||
line = self.fs.ftpnorm(line)
|
||||
basedir,basename = os.path.split(line)
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(basedir, datacr)
|
||||
self.run_as_current_user(self.fs.mkdir, path, basename)
|
||||
datacr = self.fs.get_crdata(line, mode='create')
|
||||
self.run_as_current_user(self.fs.mkdir, datacr, datacr[2])
|
||||
except IOError, err:
|
||||
self.log('FAIL MKD "%s". %s.' %(line, err.strerror))
|
||||
self.respond('550 %s.' % err.strerror)
|
||||
except OSError, err:
|
||||
why = _strerror(err)
|
||||
self.log('FAIL MKD "%s". %s.' %(line, why))
|
||||
@@ -2621,16 +2584,14 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""Remove the specified directory."""
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
if self.fs.realpath(path) == self.fs.realpath(self.fs.root):
|
||||
datacr = self.fs.get_crdata(line, mode='delete')
|
||||
if not datacr[1]:
|
||||
msg = "Can't remove root directory."
|
||||
self.respond("550 %s" %msg)
|
||||
self.log('FAIL MKD "/". %s' %msg)
|
||||
self.fs.close_cr(datacr)
|
||||
return
|
||||
self.run_as_current_user(self.fs.rmdir, path)
|
||||
self.run_as_current_user(self.fs.rmdir, datacr)
|
||||
except OSError, err:
|
||||
why = _strerror(err)
|
||||
self.log('FAIL RMD "%s". %s.' %(line, why))
|
||||
@@ -2644,10 +2605,8 @@ class FTPHandler(asynchat.async_chat):
|
||||
"""Delete the specified file."""
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
self.run_as_current_user(self.fs.remove, path)
|
||||
datacr = self.fs.get_crdata(line, mode='delete')
|
||||
self.run_as_current_user(self.fs.remove, datacr)
|
||||
except OSError, err:
|
||||
why = _strerror(err)
|
||||
self.log('FAIL DELE "%s". %s.' %(line, why))
|
||||
@@ -2662,15 +2621,13 @@ class FTPHandler(asynchat.async_chat):
|
||||
here, see RNTO command)"""
|
||||
datacr = None
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
line = self.fs.ftpnorm(line)
|
||||
path = self.fs.ftp2fs(line, datacr)
|
||||
if not self.fs.lexists(path):
|
||||
datacr = self.fs.get_crdata(line, mode='rfnr')
|
||||
if not datacr[1]:
|
||||
self.respond("550 No such file or directory.")
|
||||
elif self.fs.realpath(path) == self.fs.realpath(self.fs.root):
|
||||
elif not datacr[1]:
|
||||
self.respond("550 Can't rename the home directory.")
|
||||
else:
|
||||
self.fs.rnfr = line
|
||||
self.fs.rnfr = datacr[1]
|
||||
self.respond("350 Ready for destination name.")
|
||||
except:
|
||||
self.respond("550 Can't find the file or directory.")
|
||||
@@ -2686,21 +2643,17 @@ class FTPHandler(asynchat.async_chat):
|
||||
datacr = None
|
||||
try:
|
||||
try:
|
||||
datacr = self.fs.get_cr(line)
|
||||
src = self.fs.ftp2fs(self.fs.rnfr, datacr)
|
||||
line = self.fs.ftpnorm(line)
|
||||
basedir,basename = os.path.split(line)
|
||||
dst = self.fs.ftp2fs(basedir, datacr)
|
||||
self.run_as_current_user(self.fs.rename, src, dst,basename)
|
||||
except OSError, err:
|
||||
datacr = self.fs.get_crdata(line,'create')
|
||||
oldname = '/'.join(self.fs.rnfr.path)
|
||||
self.run_as_current_user(self.fs.rename, self.fs.rnfr, datacr)
|
||||
self.fs.rnfr = None
|
||||
self.log('OK RNFR/RNTO "%s ==> %s".' %(oldname, line))
|
||||
self.respond("250 Renaming ok.")
|
||||
except EnvironmentError, err:
|
||||
why = _strerror(err)
|
||||
self.log('FAIL RNFR/RNTO "%s ==> %s". %s.' \
|
||||
%(self.fs.ftpnorm(self.fs.rnfr), line, why))
|
||||
self.respond('550 %s.' %why)
|
||||
else:
|
||||
self.log('OK RNFR/RNTO "%s ==> %s".' \
|
||||
%(self.fs.ftpnorm(self.fs.rnfr), line))
|
||||
self.respond("250 Renaming ok.")
|
||||
finally:
|
||||
self.fs.rnfr = None
|
||||
self.fs.close_cr(datacr)
|
||||
|
||||
Reference in New Issue
Block a user