def test_has_mail_data(self) -> None: state = State() state.greeted = True state.mail_data = "" code, message = handle_mail(state, "FROM:<*****@*****.**>") assert code == SMTPStatus.BAD_SEQUENCE assert message == "Bad command sequence"
def test_empty_path(self) -> None: state = State() state.greeted = True code, message = handle_mail(state, "FROM:<>") assert code == SMTPStatus.OK assert message == "Sender OK" assert state.reverse_path == ""
def test_no_reverse_path(self) -> None: state = State() state.greeted = True state.reverse_path = None code, message = handle_rcpt(state, "TO:<*****@*****.**>") assert code == SMTPStatus.BAD_SEQUENCE assert message == "Bad command sequence"
def test_forward_paths_added(self) -> None: state = State() state.greeted = True state.reverse_path = "*****@*****.**" handle_rcpt(state, "TO:<*****@*****.**>") handle_rcpt(state, "TO:<*****@*****.**>") assert state.forward_path == ["*****@*****.**", "*****@*****.**"]
def test_with_mailbox(self) -> None: state = State() state.greeted = True code, message = handle_mail(state, "FROM:<*****@*****.**>") assert code == SMTPStatus.OK assert message == "Sender OK" assert state.reverse_path == "*****@*****.**"
def test_has_forward_path(self) -> None: state = State() state.greeted = True state.forward_path = ["*****@*****.**"] code, message = handle_mail(state, "FROM:<*****@*****.**>") assert code == SMTPStatus.BAD_SEQUENCE assert message == "Bad command sequence"
def test_response(self) -> None: state = State() state.greeted = True state.reverse_path = "*****@*****.**" code, message = handle_rcpt(state, "TO:<*****@*****.**>") assert code == SMTPStatus.OK assert message == "Receiver OK"
def test_mail_data(self) -> None: state = State() state.greeted = True state.reverse_path = "*****@*****.**" state.mail_data = "" code, message = handle_rcpt(state, "TO:<*****@*****.**>") assert code == SMTPStatus.BAD_SEQUENCE assert message == "Bad command sequence"
def test_with_arguments(self) -> None: state = State() state.greeted = True state.reverse_path = "*****@*****.**" code, message = handle_rcpt(state, "TO:<*****@*****.**> foo=bar baz") assert code == SMTPStatus.OK assert message == "Receiver OK" assert state.forward_path == ["*****@*****.**"]
def test_domain(self, getfqdn: Mock) -> None: state = State() state.greeted = False getfqdn.return_value = "smtp.example.org" code, message = handle_ehlo(state, "example.com") assert code == SMTPStatus.OK assert message == "smtp.example.org Hello example.com" assert state.greeted
def test_address_literal(self, getfqdn: Mock) -> None: state = State() state.greeted = False getfqdn.return_value = "smtp.example.org" code, message = handle_ehlo(state, "[192.168.99.22]") assert code == SMTPStatus.OK assert message == "smtp.example.org Hello [192.168.99.22]" assert state.greeted
def test_postmaster(self) -> None: state = State() state.greeted = True state.reverse_path = "*****@*****.**" code, message = handle_rcpt(state, "TO:<postMaster> foo") assert code == SMTPStatus.OK assert message == "Receiver OK" assert state.forward_path == ["postMaster"]
def test_with_arguments_and_quoted_local_part(self) -> None: state = State() state.greeted = True code, message = handle_mail( state, 'FROM:<"foo bar"@example.com> foo=bar' ) assert code == SMTPStatus.OK assert message == "Sender OK" assert state.reverse_path == '"foo bar"@example.com'
def __init__( self, reader: _StreamReaderProto, writer: _StreamWriterProto, print_mail: Callable[[State], None], ) -> None: self.reader = reader self.writer = writer self.print_mail = print_mail self.state = State()
def handle_rcpt(state: State, arguments: str) -> Reply: if arguments[:3].upper() != "TO:": return handle_wrong_arguments() try: path, rest = parse_receiver(arguments[3:]) except ValueError as exc: return handle_wrong_arguments(str(exc)) if not is_valid_smtp_arguments(rest): return handle_wrong_arguments() if not state.rcpt_allowed: return handle_bad_command_sequence() state.add_forward_path(path) return SMTPStatus.OK, "Receiver OK"
async def _handle_mail_text(self) -> None: try: await self._read_mail_text() except UnexpectedEOFError: pass except ValueError: self._write_reply(SMTPStatus.SYNTAX_ERROR, "Line too long.") else: self._write_reply(SMTPStatus.OK, "OK") self.state.date = datetime.datetime.utcnow() state = self.state self.print_mail(state) self.state = State() self.state.greeted = state.greeted
def handle_helo(state: State, arguments: str) -> Reply: if not arguments.strip(): return handle_missing_arguments() if not is_valid_domain(arguments): return handle_wrong_arguments() state.greeted = True return SMTPStatus.OK, f"{getfqdn()} Hello {arguments}"
def handle_mail(state: State, arguments: str) -> Reply: if arguments[:5].upper() != "FROM:": return handle_wrong_arguments() try: path, rest = parse_reverse_path(arguments[5:]) except ValueError as exc: return handle_wrong_arguments(str(exc)) if not is_valid_smtp_arguments(rest): return handle_wrong_arguments() if not state.greeted: return handle_no_greeting() if not state.mail_allowed: return handle_bad_command_sequence() state.clear() state.reverse_path = path return SMTPStatus.OK, "Sender OK"
def test_print(self) -> None: out = StringIO() state = State() state.date = datetime.datetime(2017, 6, 4, 14, 34, 15) state.reverse_path = "*****@*****.**" state.forward_path = ["*****@*****.**", "*****@*****.**"] state.mail_data = "Subject: Foo\r\n\r\nText\r\n" write_mbox_mail(out, state) assert out.getvalue() == ( "From [email protected] Sun Jun 4 14:34:15 2017\n" "X-FakeSMTPd-Receiver: [email protected]\n" "X-FakeSMTPd-Receiver: [email protected]\n" "Subject: Foo\n" "\n" "Text\n" "\n" )
def test_not_greeted(self) -> None: state = State() state.greeted = False code, message = handle_mail(state, "FROM:<*****@*****.**>") assert code == SMTPStatus.BAD_SEQUENCE assert message == "No EHLO sent"
def test_local_part_too_long(self) -> None: code, message = handle_rcpt( State(), f"TO:<{'a' * (SMTP_LOCAL_PART_LIMIT + 1)}@example.com>" ) assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Path too long"
def test_invalid_domain(self) -> None: code, message = handle_helo(State(), "*") assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Syntax error in arguments"
def test_empty_path(self) -> None: code, message = handle_rcpt(State(), "TO:<>") assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Syntax error in arguments"
def test_response(self, getfqdn: Mock) -> None: getfqdn.return_value = "smtp.example.org" code, message = handle_helo(State(), "example.com") assert code == SMTPStatus.OK assert message == "smtp.example.org Hello example.com"
def test_no_argument(self) -> None: code, message = handle_helo(State(), "") assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Missing arguments"
def test_set_greeted(self) -> None: state = State() state.greeted = False handle_helo(state, "example.com") assert state.greeted
def test_path_too_long(self) -> None: code, message = handle_rcpt( State(), f"TO:<{'a' * 60}@{'a' * (SMTP_PATH_LIMIT - 61)}>" ) assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Path too long"
def test_domain_too_long(self) -> None: code, message = handle_rcpt( State(), f"TO:<foo@{'a' * (SMTP_DOMAIN_LIMIT + 1)}>" ) assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Path too long"
def test_path_with_trailing_chars(self) -> None: code, message = handle_rcpt(State(), "TO:<*****@*****.**>foo=bar") assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Syntax error in arguments"
def test_invalid_argument(self) -> None: code, message = handle_rcpt(State(), "TO:<*****@*****.**> -foo") assert code == SMTPStatus.SYNTAX_ERROR_IN_PARAMETERS assert message == "Syntax error in arguments"