From 5cb5166aa8e4a5296bd2e2029d5e804aa99f6ec2 Mon Sep 17 00:00:00 2001 From: "willmcgugan@gmail.com" Date: Mon, 9 Sep 2013 21:04:18 +0000 Subject: Added option to folllow symlinks to ftpfs, patch from Andrew git-svn-id: http://pyfilesystem.googlecode.com/svn/trunk@873 67cdc799-7952-0410-af00-57a81ceafa0f --- fs/ftpfs.py | 28 +++++++++++++++++++++++++++- fs/opener.py | 2 +- fs/tests/test_ftpfs.py | 30 ++++++++++++++++-------------- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/fs/ftpfs.py b/fs/ftpfs.py index 2527285..4c3f8b9 100644 --- a/fs/ftpfs.py +++ b/fs/ftpfs.py @@ -337,6 +337,7 @@ class FTPListDataParser(object): i = 0 while (i + 3) < len(result.name): if result.name[i:i+4] == ' -> ': + result.target = result.name[i+4:] result.name = result.name[:i] break i += 1 @@ -891,7 +892,7 @@ class FTPFS(FS): 'file.read_and_write' : False, } - def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT, port=21, dircache=True): + def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT, port=21, dircache=True, follow_symlinks=False): """Connect to a FTP server. :param host: Host to connect to @@ -917,6 +918,7 @@ class FTPFS(FS): self.timeout = timeout self.default_timeout = timeout is _GLOBAL_DEFAULT_TIMEOUT self.use_dircache = dircache + self.follow_symlinks = follow_symlinks self.use_mlst = False self._lock = threading.RLock() @@ -1018,6 +1020,30 @@ class FTPFS(FS): pass self.dircache[path] = dirlist + def is_symlink(info): + return info['try_retr'] and info['try_cwd'] and info.has_key('target') + + def resolve_symlink(linkpath): + linkinfo = self.getinfo(linkpath) + if not linkinfo.has_key('resolved'): + linkinfo['resolved'] = linkpath + if is_symlink(linkinfo): + target = linkinfo['target'] + base, fname = pathsplit(linkpath) + return resolve_symlink(pathjoin(base, target)) + else: + return linkinfo + + if self.follow_symlinks: + for name in dirlist: + if is_symlink(dirlist[name]): + target = dirlist[name]['target'] + linkinfo = resolve_symlink(pathjoin(path, target)) + for key in linkinfo: + if key != 'name': + dirlist[name][key] = linkinfo[key] + del dirlist[name]['target'] + return dirlist @synchronize diff --git a/fs/opener.py b/fs/opener.py index 83e9ce1..49fef9b 100644 --- a/fs/opener.py +++ b/fs/opener.py @@ -434,7 +434,7 @@ examples: dirpath, resourcepath = pathsplit(path) url = netloc - ftpfs = FTPFS(url, user=username or '', passwd=password or '') + ftpfs = FTPFS(url, user=username or '', passwd=password or '', follow_symlinks=(fs_name_params == "symlinks")) ftpfs.cache_hint(True) if create_dir and path: diff --git a/fs/tests/test_ftpfs.py b/fs/tests/test_ftpfs.py index 588367f..d37e5df 100644 --- a/fs/tests/test_ftpfs.py +++ b/fs/tests/test_ftpfs.py @@ -16,7 +16,9 @@ from six import PY3 try: - from pyftpdlib import ftpserver + from pyftpdlib.authorizers import DummyAuthorizer + from pyftpdlib.handlers import FTPHandler + from pyftpdlib.servers import FTPServer except ImportError: if not PY3: raise ImportError("Requires pyftpdlib ") @@ -27,7 +29,7 @@ from fs import ftpfs ftp_port = 30000 class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases): - + __test__ = not PY3 def setUp(self): @@ -47,11 +49,11 @@ class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases): file_path, self.temp_dir, use_port], - stdout=subprocess.PIPE, + stdout=subprocess.PIPE, env=env) # Block until the server writes a line to stdout self.ftp_server.stdout.readline() - + # Poll until a connection can be made start_time = time.time() while time.time() - start_time < 5: @@ -66,14 +68,14 @@ class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases): else: # Avoid a possible infinite loop raise Exception("Unable to connect to ftp server") - + self.fs = ftpfs.FTPFS('127.0.0.1', 'user', '12345', dircache=True, port=use_port, timeout=5.0) self.fs.cache_hint(True) def tearDown(self): - #self.ftp_server.terminate() - if sys.platform == 'win32': + #self.ftp_server.terminate() + if sys.platform == 'win32': os.popen('TASKKILL /PID '+str(self.ftp_server.pid)+' /F') else: os.system('kill '+str(self.ftp_server.pid)) @@ -89,21 +91,21 @@ if __name__ == "__main__": # Run an ftp server that exposes a given directory import sys - authorizer = ftpserver.DummyAuthorizer() + authorizer = DummyAuthorizer() authorizer.add_user("user", "12345", sys.argv[1], perm="elradfmw") authorizer.add_anonymous(sys.argv[1]) - def nolog(*args): - pass - ftpserver.log = nolog - ftpserver.logline = nolog + #def nolog(*args): + # pass + #ftpserver.log = nolog + #ftpserver.logline = nolog - handler = ftpserver.FTPHandler + handler = FTPHandler handler.authorizer = authorizer address = ("127.0.0.1", int(sys.argv[2])) #print address - ftpd = ftpserver.FTPServer(address, handler) + ftpd = FTPServer(address, handler) sys.stdout.write('serving\n') sys.stdout.flush() -- cgit v1.2.1