def handle_command(line, mail_state): """Handle each SMTP command as it's sent to the server The paramater 'line' is the currently stream of data ending in '\\n'. 'mail_state' is an instance of 'hoard.state.MailState'. """ close = False if mail_state.reading: resp = None # Not exactly nice but it's only way I could safely figure # out if it was the \n.\n if line[0] == "." and len(line) == 3 and ord(line[0]) == 46: mail_state.reading = False mail_state.write_to_db() resp = response() elif line.lower().startswith("ehlo"): resp = ["%s\n" % x for x in EHLO_RESPONSES] elif any(line.lower().startswith(e) for e in ['helo', 'mail from', 'rcpt to', 'noop']): resp = response(250) elif line.lower().startswith("rset"): new_id = email_id() log.debug("[%s] RSET received, changing ID to [%s]" % (mail_state.email_id, new_id)) # Reset mail state mail_state.reading = False mail_state.email_id = new_id resp = response(250) elif line.lower().startswith("starttls"): resp = response(220) elif line.lower().startswith("vrfy"): resp = response(252) elif line.lower().startswith("quit"): resp = response(221) close = True elif line.lower().startswith("data"): resp = response(354) mail_state.reading = True else: resp = response(500) return resp, close
def connection_ready(sock, fd, events): """ Accepts the socket connections and passes them off to be handled. 'sock' is an instance of 'socket'. 'fd' is an open file descriptor for the current connection. 'events' is an integer of the number of events on the socket. """ while True: try: connection, address = sock.accept() except socket.error as e: if e.errno not in (errno.EWOULDBLOCK, errno.EAGAIN): raise return log.debug("Connection from '%s'" % address[0]) connection.setblocking(0) stream = connection_stream(connection) if not stream: return mail_state = MailState() mail_state.address = address[0] mail_state.email_id = email_id() # Sadly there is nothing I can do about the handle and loop # fuctions. They have to exist within connection_ready def handle(line): """ Handle a line of socket data, figure out if it's a valid SMTP keyword and handle it accordingly. """ log.debug("[%s] RECV: %s" % (mail_state.email_id, line.rstrip())) mail_state.commands = line.rstrip() resp, close = handle_command(line, mail_state) if resp: if isinstance(resp, list): # This is required for returning # multiple status codes for EHLO for r in resp: try: write_response(stream, mail_state, r) except iostream.StreamClosedError: return else: # Otherwise it's a single response try: write_response(stream, mail_state, resp) except iostream.StreamClosedError: return if close is True: log.debug("Closing") stream.close() return else: loop() def loop(): """ Loop over the socket data until we receive a newline character (\n) """ stream.read_until("\n", handle) stream.write(response(220)) loop()