Last active
February 5, 2018 12:58
-
-
Save gfranxman/7087471 to your computer and use it in GitHub Desktop.
twisted ftp server with hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Twisted Sports FTP server, simple auth and post upload hook. | |
NOTE: In the spirit of doing it the hard way first, it now feels like I should have used | |
reactor.addWriter to install the HookFileWriter instead of these acrobatics. | |
Does the reactor really hold the filewriter for these tasks? | |
""" | |
from twisted.protocols.ftp import FTPFactory, FTPRealm, errnoToFailure | |
from twisted.cred.portal import Portal | |
from twisted.cred.checkers import AllowAnonymousAccess, FilePasswordDB | |
from twisted.internet import reactor | |
from twisted.protocols.ftp import IFTPShell, FTPAnonymousShell, FTPShell, _FileWriter, checkers, defer | |
def myHook( obj ): | |
print "processing", obj.fObj.name | |
return "done" | |
class HookFileWriter( _FileWriter ): | |
def __init__( self, fObj, callback ): | |
super( HookFileWriter, self ).__init__( fObj ) | |
self.callback = callback | |
def close( self ): | |
res = super( HookFileWriter, self ).close() | |
print "********************** calling the hook ********************" | |
if( self.callback ): | |
cbres = self.callback( self ) | |
print "callback res", cbres | |
return res | |
class HookShell( FTPShell ): | |
""" this shell extends the normal shell, but it's writer will run a hook after the transfer is complete. | |
""" | |
def __init__( self, filesystemRoot, callback ): | |
super( HookShell, self ).__init__( filesystemRoot ) | |
self.callback = callback | |
def openForWriting(self, path): | |
""" | |
Open C{path} for writing. | |
@param path: The path, as a list of segments, to open. | |
@type path: C{list} of C{unicode} | |
@return: A L{Deferred} is returned that will fire with an object | |
implementing L{IWriteFile} if the file is successfully opened. If | |
C{path} is a directory, or if an exception is raised while trying | |
to open the file, the L{Deferred} will fire with an error. | |
""" | |
p = self._path(path) | |
if p.isdir(): | |
# Normally, we would only check for EISDIR in open, but win32 | |
# returns EACCES in this case, so we check before | |
return defer.fail(IsADirectoryError(path)) | |
try: | |
fObj = p.open('w') | |
except (IOError, OSError), e: | |
return errnoToFailure(e.errno, path) | |
except: | |
return defer.fail() | |
print "about to defer to the _FileWriter, should use the HookedFileWriter" | |
#return defer.succeed(_FileWriter(fObj)) | |
return defer.succeed( HookFileWriter(fObj, self.callback) ) | |
class HookRealm( FTPRealm ): | |
""" the realm creates the FTPShell which has the _path(p) method that saves files. | |
we are going to use our own Shell that has a hook for processing the upload. | |
""" | |
def __init__( self, anonymousRoot, userHome="/home", callback=None ): | |
#super( HookRealm, self).__init__( anonymousRoot, userHome=userHome ) | |
FTPRealm.__init__( self, anonymousRoot, userHome=userHome ) | |
self.callback = callback | |
def requestAvatar(self, avatarId, mind, *interfaces): | |
for iface in interfaces: | |
if iface is IFTPShell: | |
if avatarId is checkers.ANONYMOUS: | |
avatar = FTPAnonymousShell(self.anonymousRoot) | |
else: | |
#avatar = FTPShell(self.getHomeDirectory(avatarId)) | |
avatar = HookShell(self.getHomeDirectory(avatarId), self.callback) | |
return (IFTPShell, avatar, | |
getattr(avatar, 'logout', lambda: None)) | |
raise NotImplementedError( | |
"Only IFTPShell interface is supported by this realm") | |
############## boilerplate server using our HookRealm and callback | |
p = Portal( HookRealm('/no_anon_access/', userHome="/tmp/", callback=myHook), | |
#[AllowAnonymousAccess(), FilePasswordDB("pass.dat")]) | |
[FilePasswordDB("pass.dat"), ]) | |
# | |
# Once the portal is set up, start up the FTPFactory and pass the portal to | |
# it on startup. FTPFactory will start up a twisted.protocols.ftp.FTP() | |
# handler for each incoming OPEN request. Business as usual in Twisted land. | |
# | |
f = FTPFactory( p ) | |
f.allowAnonymous = False | |
f.welcomeMessage = "TwistedSportsFTP 0.1" | |
# | |
# You know this part. Point the reactor to port 21 coupled with the above factory, | |
# and start the event loop. | |
# | |
reactor.listenTCP(9021, f) | |
reactor.run() | |
# Donez0rs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This uses a local file called pass.dat which should be plaintext [user]:[password]
and there should be a directory called /tmp/[user]