Beispiel #1
0
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
Beispiel #2
0
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()