summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbtimby <btimby@67cdc799-7952-0410-af00-57a81ceafa0f>2012-04-17 15:47:48 +0000
committerbtimby <btimby@67cdc799-7952-0410-af00-57a81ceafa0f>2012-04-17 15:47:48 +0000
commitfd93dcc55e5218ba57ee418faf1e08f2a2bd6f97 (patch)
tree5f1e4b7b6f8f40bfe5d1bfc603b62aa9f025f49d
parent831618e29f1033b4fe8db14058ab635643273c62 (diff)
downloadpyfilesystem-fd93dcc55e5218ba57ee418faf1e08f2a2bd6f97.tar.gz
Initial implementation of pyftpdlib integration
git-svn-id: http://pyfilesystem.googlecode.com/svn/trunk@765 67cdc799-7952-0410-af00-57a81ceafa0f
-rw-r--r--fs/commands/fsserve.py7
-rw-r--r--fs/expose/ftp.py136
2 files changed, 143 insertions, 0 deletions
diff --git a/fs/commands/fsserve.py b/fs/commands/fsserve.py
index 5a05dd8..4595c15 100644
--- a/fs/commands/fsserve.py
+++ b/fs/commands/fsserve.py
@@ -56,6 +56,13 @@ Serves the contents of PATH with one of a number of methods"""
self.output("Starting rpc server on %s:%i\n" % (options.addr, port), verbose=True)
s.serve_forever()
+ elif options.type == 'ftp':
+ from fs.expose.ftp import serve_fs
+ if port is None:
+ port = 21
+ self.output("Starting ftp server on %s:%i\n" % (options.addr, port), verbose=True)
+ serve_fs(fs, options.addr, port)
+
elif options.type == 'sftp':
from fs.expose.sftp import BaseSFTPServer
import logging
diff --git a/fs/expose/ftp.py b/fs/expose/ftp.py
new file mode 100644
index 0000000..2bdaf1d
--- /dev/null
+++ b/fs/expose/ftp.py
@@ -0,0 +1,136 @@
+"""
+fs.expose.ftp
+==============
+
+Expose an FS object over FTP (via pyftpdlib).
+
+This module provides the necessary interfaces to expose an FS object over
+FTP, plugging into the infrastructure provided by the 'pyftpdlib' module.
+"""
+
+from __future__ import with_statement
+
+import os
+import stat as statinfo
+import time
+import threading
+
+from pyftpdlib import ftpserver
+
+from fs.base import flags_to_mode
+from fs.path import *
+from fs.errors import *
+from fs.local_functools import wraps
+from fs.filelike import StringIO
+from fs.utils import isdir
+from fs.osfs import OSFS
+
+class FTPFS(ftpserver.AbstractedFS):
+ def __init__(self, fs, root, cmd_channel):
+ self.fs = fs
+ super(FTPFS, self).__init__(root, cmd_channel)
+
+ def validpath(self, path):
+ return True
+
+ def open(self, path, mode):
+ return self.fs.open(path, mode)
+
+ def chdir(self, path):
+ self.cwd = path
+
+ def mkdir(self, path):
+ if isinstance(path, str):
+ path = unicode(path, sys.getfilesystemencoding())
+ self.fs.createdir(path)
+
+ def listdir(self, path):
+ return map(lambda x: x.encode('utf8'), self.fs.listdir(path))
+
+ def rmdir(self, path):
+ self.fs.removedir(path)
+
+ def remove(self, path):
+ self.fs.remove(path)
+
+ def rename(self, src, dst):
+ self.fs.rename(src, dst)
+
+ def chmod(self, path, mode):
+ raise NotImplementedError()
+
+ def stat(self, path):
+ # TODO: stat needs to be handled using fs.getinfo() method.
+ return super(FTPFS, self).stat(self.fs.getsyspath(path))
+
+ def lstat(self, path):
+ return self.stat(path)
+
+ def isfile(self, path):
+ return self.fs.isfile(path)
+
+ def isdir(self, path):
+ return self.fs.isdir(path)
+
+ def getsize(self, path):
+ return self.fs.getsize(path)
+
+ def getmtime(self, path):
+ return self.fs.getinfo(path).time
+
+ def realpath(self, path):
+ return path
+
+ def lexists(self, path):
+ return True
+
+
+class FTPFSFactory(object):
+ """
+ A factory class which can hold a reference to a file system object and
+ later pass it along to an FTPFS instance. An instance of this object allows
+ multiple FTPFS instances to be created by pyftpdlib and share the same fs.
+ """
+ def __init__(self, fs):
+ """
+ Initializes the factory with an fs instance.
+ """
+ self.fs = fs
+
+ def __call__(self, root, cmd_channel):
+ """
+ This is the entry point of pyftpdlib. We will pass along the two parameters
+ as well as the previously provided fs instance.
+ """
+ return FTPFS(self.fs, root, cmd_channel)
+
+
+class HomeFTPFS(FTPFS):
+ """
+ A file system which serves a user's home directory.
+ """
+ def __init__(self, root, cmd_channel):
+ """
+ Use the provided user's home directory to create an FTPFS that serves an OSFS
+ rooted at the home directory.
+ """
+ super(DemoFS, self).__init__(OSFS(root_path=root), '/', cmd_channel)
+
+
+def serve_fs(fs, addr, port):
+ from pyftpdlib.contrib.authorizers import UnixAuthorizer
+ ftp_handler = ftpserver.FTPHandler
+ ftp_handler.authorizer = ftpserver.DummyAuthorizer()
+ ftp_handler.authorizer.add_anonymous('/')
+ ftp_handler.abstracted_fs = FTPFSFactory(fs)
+ s = ftpserver.FTPServer((addr, port), ftp_handler)
+ s.serve_forever()
+
+
+def main():
+ serve_fs(HomeFTPFS, '127.0.0.1', 21)
+
+
+# When called from the command-line, expose a DemoFS for testing purposes
+if __name__ == "__main__":
+ main()