2
from cStringIO import StringIO
4
from twisted.protocols.ftp import IFTPShell, IReadFile, IWriteFile
5
from twisted.internet import defer
6
from twisted.internet.interfaces import IConsumer
7
from twisted.web2.stream import StreamProducer, IByteStream
9
from zope.interface import implements
11
from twisted.python import components
12
from twisted.vfs import ivfs, pathutils
14
# XXX: Import this to make sure the adapter registration has happened.
15
from twisted.vfs.adapters import stream
18
class FileSystemToIFTPShellAdaptor:
22
def __init__(self, filesystem):
23
self.filesystem = filesystem
25
def _makePath(segments):
26
return '/'.join(segments)
27
_makePath = staticmethod(_makePath)
29
def makeDirectory(self, path):
30
dirname, basename = path[:-1], path[-1]
31
parent = self.filesystem.fetch(self._makePath(dirname))
33
parent.createDirectory(basename)
37
return defer.succeed(None)
39
def removeDirectory(self, path):
41
node = self.filesystem.fetch(self._makePath(path))
42
if not ivfs.IFileSystemContainer.providedBy(node):
43
raise IOError("removeDirectory can only remove directories.")
48
return defer.succeed(None)
50
def removeFile(self, path):
52
node = self.filesystem.fetch(self._makePath(path))
53
if ivfs.IFileSystemContainer.providedBy(node):
54
raise IOError("removeFile cannot remove directories.")
59
return defer.succeed(None)
61
def list(self, path, keys=()):
62
node = self.filesystem.fetch(self._makePath(path))
65
for childName, childNode in node.children():
66
attrs = self._attrify(childNode)
67
result.append((childName, [attrs[attrName] for attrName in keys]))
69
return defer.succeed(result)
71
# XXX - this should probably go in a helper somewhere
72
def _attrify(self, node):
73
meta = node.getMetadata()
74
permissions = meta.get('permissions', None)
75
directory = ivfs.IFileSystemContainer.providedBy(node)
76
if permissions is None:
78
if ivfs.IFileSystemContainer.providedBy(node):
83
return {'permissions': permissions,
84
'directory': directory,
85
'size': meta.get('size', 0),
86
'owner': str(meta.get('uid', 'user')),
87
'group': str(meta.get('gid', 'user')),
88
'modified': meta.get('mtime', time.time()),
89
'hardlinks': meta.get('nlink', 1)
92
def access(self, path):
93
# XXX: stubbed out to always succeed.
94
return defer.succeed(None)
96
def openForReading(self, segs):
97
node = self.filesystem.fetch(self._makePath(segs))
98
frvfs = FTPReadVFS(node)
99
return defer.succeed(frvfs)
101
def openForWriting(self, segs):
102
# XXX: this method is way too ugly
103
dirname, basename = segs[:-1], segs[-1]
104
node = self.filesystem.fetch(
105
self._makePath(dirname)).createFile(basename)
106
fwvfs = FTPWriteVFS(node)
107
return defer.succeed(fwvfs)
109
def stat(self, path, keys=()):
110
node = self.filesystem.fetch(self._makePath(path))
111
attrs = self._attrify(node)
112
return defer.succeed([attrs[attrName] for attrName in keys])
114
def rename(self, from_, to):
115
assert len(to) == 1, (
116
"renaming into other directories isn't supported yet.")
118
self.filesystem.fetch(self._makePath(from_)).rename(to[0])
122
return defer.succeed(None)
125
class FTPReadVFS(object):
126
implements(IReadFile)
128
def __init__(self, node):
131
def send(self, consumer):
132
return StreamProducer(IByteStream(self.node)).beginProducing(consumer)
135
class FTPWriteVFS(object):
136
implements(IWriteFile)
138
def __init__(self, node):
142
return defer.succeed(IConsumer(self.node))
145
class _FileToConsumerAdapter:
146
implements(IConsumer)
148
def __init__(self, original):
149
self.original = original
152
def write(self, bytes):
153
self.original.writeChunk(self.offset, bytes)
154
self.offset += len(bytes)
158
components.registerAdapter(FileSystemToIFTPShellAdaptor,
159
pathutils.IFileSystem, IFTPShell)
160
components.registerAdapter(_FileToConsumerAdapter,
161
ivfs.IFileSystemLeaf, IConsumer)