def __init__(self, clientSocket, serverName, serverPort): Dibbler.BrighterAsyncChat.__init__(self, clientSocket) self.request = '' self.set_terminator('\r\n') self.command = '' # The SMTP command being processed... self.args = '' # ...and its arguments self.isClosing = False # Has the server closed the socket? self.inData = False self.data = "" self.blockData = False if not self.onIncomingConnection(clientSocket): # We must refuse this connection, so pass an error back # to the mail client. self.push("421 Connection not allowed\r\n") self.close_when_done() return self.serverSocket = ServerLineReader(serverName, serverPort, self.onServerLine)
class SMTPProxyBase(Dibbler.BrighterAsyncChat): """An async dispatcher that understands SMTP and proxies to a SMTP server, calling `self.onTransaction(command, args)` for each transaction. self.onTransaction() should return the command to pass to the proxied server - the command can be the verbatim command or a processed version of it. The special command 'KILL' kills it (passing a 'QUIT' command to the server). """ def __init__(self, clientSocket, serverName, serverPort): Dibbler.BrighterAsyncChat.__init__(self, clientSocket) self.request = '' self.set_terminator('\r\n') self.command = '' # The SMTP command being processed... self.args = '' # ...and its arguments self.isClosing = False # Has the server closed the socket? self.inData = False self.data = "" self.blockData = False if not self.onIncomingConnection(clientSocket): # We must refuse this connection, so pass an error back # to the mail client. self.push("421 Connection not allowed\r\n") self.close_when_done() return self.serverSocket = ServerLineReader(serverName, serverPort, self.onServerLine) def onIncomingConnection(self, clientSocket): """Checks the security settings.""" # Stolen from UserInterface.py #remoteIP = clientSocket.getpeername()[0] #trustedIPs = options["smtpproxy", "allow_remote_connections"] #if trustedIPs == "*" or remoteIP == clientSocket.getsockname()[0]: # return True #trustedIPs = trustedIPs.replace('.', '\.').replace('*', '([01]?\d\d?|2[04]\d|25[0-5])') #for trusted in trustedIPs.split(','): # if re.search("^" + trusted + "$", remoteIP): # return True #return False return True def onTransaction(self, command, args): """Overide this. Takes the raw command and returns the (possibly processed) command to pass to the email client.""" raise NotImplementedError def onProcessData(self, data): """Overide this. Takes the raw data and returns the (possibly processed) data to pass back to the email client.""" raise NotImplementedError def onServerLine(self, line): """A line of response has been received from the SMTP server.""" # Has the server closed its end of the socket? if not line: self.isClosing = True # We don't process the return, just echo the response. self.push(line) self.onResponse() def collect_incoming_data(self, data): """Asynchat override.""" self.request = self.request + data def found_terminator(self): """Asynchat override.""" verb = self.request.strip().upper() if verb == 'KILL': self.socket.shutdown(2) self.close() raise SystemExit if self.request.strip() == '': # Someone just hit the Enter key. self.command = self.args = '' else: # A proper command. if self.request[:10].upper() == "MAIL FROM:": splitCommand = self.request.split(":", 1) elif self.request[:8].upper() == "RCPT TO:": splitCommand = self.request.split(":", 1) else: splitCommand = self.request.strip().split(None, 1) self.command = splitCommand[0] self.args = splitCommand[1:] if self.inData == True: self.data += self.request + '\r\n' if self.request == ".": self.inData = False cooked = self.onProcessData(self.data) self.data = "" if self.blockData == False: self.serverSocket.push(cooked) else: self.push("250 OK\r\n") else: cooked = self.onTransaction(self.command, self.args) if cooked is not None: self.serverSocket.push(cooked + '\r\n') self.command = self.args = self.request = '' def onResponse(self): # If onServerLine() decided that the server has closed its # socket, close this one when the response has been sent. if self.isClosing: self.close_when_done() # Reset. self.command = '' self.args = '' self.isClosing = False