summaryrefslogtreecommitdiff
path: root/addons/document_ftp/ftpserver/ftpserver.py
diff options
context:
space:
mode:
authorHarry (OpenERP) <hmo@tinyerp.com>2010-07-11 11:36:46 +0530
committerHarry (OpenERP) <hmo@tinyerp.com>2010-07-11 11:36:46 +0530
commitfad9edc19eb7e0ac32aab96f6f299e328b6574f2 (patch)
tree6279c86757be0f9754e2c33d0a1d6388778084d4 /addons/document_ftp/ftpserver/ftpserver.py
parent6ccff1aab8992bd56a9e0ac45ad5988b549ab530 (diff)
parentd6f1e1f29f8110c363d8a413da8dcf17284cc205 (diff)
[MERGE]
bzr revid: hmo@tinyerp.com-20100711060646-gwp6039vsln1x30a
Diffstat (limited to 'addons/document_ftp/ftpserver/ftpserver.py')
-rw-r--r--addons/document_ftp/ftpserver/ftpserver.py315
1 files changed, 152 insertions, 163 deletions
diff --git a/addons/document_ftp/ftpserver/ftpserver.py b/addons/document_ftp/ftpserver/ftpserver.py
index 3a85b3061a7..e267216ddec 100644
--- a/addons/document_ftp/ftpserver/ftpserver.py
+++ b/addons/document_ftp/ftpserver/ftpserver.py
@@ -221,6 +221,33 @@ def _strerror(err):
else:
return err.strerror
+def _to_unicode(s):
+ try:
+ return s.decode('utf-8')
+ except UnicodeError:
+ pass
+ try:
+ return s.decode('latin')
+ except UnicodeError:
+ pass
+ try:
+ return s.encode('ascii')
+ except UnicodeError:
+ return s
+
+def _to_decode(s):
+ try:
+ return s.encode('utf-8')
+ except UnicodeError:
+ pass
+ try:
+ return s.encode('latin')
+ except UnicodeError:
+ pass
+ try:
+ return s.decode('ascii')
+ except UnicodeError:
+ return s
# --- library defined exceptions
@@ -793,7 +820,7 @@ class DTPHandler(asyncore.dispatcher):
if self.transfer_finished:
self.cmd_channel.respond("226 Transfer complete.")
if self.file_obj:
- fname = self.cmd_channel.fs.fs2ftp(self.file_obj.name)
+ fname = self.file_obj.name
self.cmd_channel.log('"%s" %s.' %(fname, action))
else:
tot_bytes = self.get_transmitted_bytes()
@@ -1138,19 +1165,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 +1610,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 +1624,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 +2034,71 @@ 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]):
+ nodelist = self.run_as_current_user(self.fs.listdir, datacr)
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]
+ nodelist = [datacr[1],]
+
+ listing = []
+ for nl in nodelist:
+ if isinstance(nl.path, (list, tuple)):
+ listing.append(nl.path[-1])
+ else:
+ listing.append(nl.path) # assume string
+ 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 = ''.join([ _to_decode(x) + '\r\n' for x in listing ])
+ self.log('OK NLST "%s". Transfer starting.' %line)
+ self.push_dtp_data(data)
# --- MLST and MLSD commands
@@ -2096,18 +2113,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 +2143,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 +2163,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 +2173,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 +2185,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 +2198,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 +2228,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 +2241,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 +2257,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 +2299,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 +2503,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 +2528,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 +2556,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 +2579,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 +2617,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 +2638,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 +2654,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 +2676,20 @@ 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 = self.fs.rnfr.path
+ if isinstance(oldname, (list, tuple)):
+ oldname = '/'.join(oldname)
+ self.run_as_current_user(self.fs.rename, self.fs.rnfr, datacr)
+ self.fs.rnfr = None
+ self.log('OK RNFR/RNTO "%s ==> %s".' % \
+ (_to_unicode(oldname), _to_unicode(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))
+ % (_to_unicode(oldname), _to_unicode(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)