Loading mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParser.kt +14 −9 Original line number Diff line number Diff line Loading @@ -115,20 +115,25 @@ internal class SmtpResponseParser( private fun parseEhloLine(ehloLine: String, keywords: MutableMap<String, List<String>>) { val parts = ehloLine.split(" ") try { val keyword = checkAndNormalizeEhloKeyword(parts[0]) val parameters = checkEhloParameters(parts) if (keywords.containsKey(keyword)) { parserError("Same EHLO keyword present in more than one response line") parserError("Same EHLO keyword present in more than one response line", logging = false) } keywords[keyword] = parameters } catch (e: SmtpResponseParserException) { logger.log(e, "Ignoring EHLO keyword line: $ehloLine") } } private fun checkAndNormalizeEhloKeyword(text: String): String { val keyword = text.uppercase() if (!keyword[0].isCapitalAlphaDigit() || keyword.any { !it.isCapitalAlphaDigit() && it != DASH }) { parserError("EHLO keyword contains invalid character") parserError("EHLO keyword contains invalid character", logging = false) } return keyword Loading @@ -138,9 +143,9 @@ internal class SmtpResponseParser( for (i in 1..parts.lastIndex) { val parameter = parts[i] if (parameter.isEmpty()) { parserError("EHLO parameter must not be empty") parserError("EHLO parameter must not be empty", logging = false) } else if (parameter.any { it.code !in 33..126 }) { parserError("EHLO parameter contains invalid character") parserError("EHLO parameter contains invalid character", logging = false) } } Loading mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserTest.kt +40 −37 Original line number Diff line number Diff line Loading @@ -145,26 +145,36 @@ class SmtpResponseParserTest { } @Test fun `read EHLO response with invalid keyword`() { fun `read EHLO response with invalid keywords`() { val input = """ 250-smtp.domain.example 250 KEY:WORD 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-PIPE_CONNECT 250-AUTH=PLAIN 250 HELP """.toPeekableInputStream() val parser = SmtpResponseParser(logger, input) assertFailsWithMessage("EHLO keyword contains invalid character") { parser.readHelloResponse() val response = parser.readHelloResponse() assertType<SmtpHelloResponse.Hello>(response) { hello -> assertThat(hello.keywords.keys).containsExactly( "SIZE", "8BITMIME", "PIPELINING", "HELP" ) } assertThat(logger.logEntries).containsExactly( LogEntry( throwable = null, message = """ SMTP response data on parser error: 250-smtp.domain.example 250 KEY:WORD """.trimIndent() assertThat(logger.logEntries.map { it.message }).containsExactly( "Ignoring EHLO keyword line: PIPE_CONNECT", "Ignoring EHLO keyword line: AUTH=PLAIN" ) assertThat(logger.logEntries.map { it.throwable?.message }).containsExactly( "EHLO keyword contains invalid character", "EHLO keyword contains invalid character" ) } Loading @@ -176,44 +186,37 @@ class SmtpResponseParserTest { """.toPeekableInputStream() val parser = SmtpResponseParser(logger, input) assertFailsWithMessage("EHLO parameter must not be empty") { parser.readHelloResponse() val response = parser.readHelloResponse() assertType<SmtpHelloResponse.Hello>(response) { hello -> assertThat(hello.keywords.keys).isEmpty() } assertThat(logger.logEntries).containsExactly( LogEntry( throwable = null, message = """ SMTP response data on parser error: 250-smtp.domain.example 250 KEYWORD${" "} """.trimIndent() ) ) assertThat(logger.logEntries).hasSize(1) assertThat(logger.logEntries.first().throwable).hasMessageThat().isEqualTo("EHLO parameter must not be empty") assertThat(logger.logEntries.first().message).isEqualTo("Ignoring EHLO keyword line: KEYWORD ") } @Test fun `read EHLO response with invalid parameter`() { val input = """ 250-smtp.domain.example 250-8BITMIME 250 KEYWORD para${"\t"}meter """.toPeekableInputStream() val parser = SmtpResponseParser(logger, input) assertFailsWithMessage("EHLO parameter contains invalid character") { parser.readHelloResponse() val response = parser.readHelloResponse() assertType<SmtpHelloResponse.Hello>(response) { hello -> assertThat(hello.keywords.keys).containsExactly("8BITMIME") } assertThat(logger.logEntries).containsExactly( LogEntry( throwable = null, message = """ SMTP response data on parser error: 250-smtp.domain.example 250 KEYWORD para${"\t"}meter """.trimIndent() ) ) assertThat(logger.logEntries).hasSize(1) assertThat(logger.logEntries.first().throwable) .hasMessageThat().isEqualTo("EHLO parameter contains invalid character") assertThat(logger.logEntries.first().message) .isEqualTo("Ignoring EHLO keyword line: KEYWORD para${"\t"}meter") } @Test Loading Loading
mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParser.kt +14 −9 Original line number Diff line number Diff line Loading @@ -115,20 +115,25 @@ internal class SmtpResponseParser( private fun parseEhloLine(ehloLine: String, keywords: MutableMap<String, List<String>>) { val parts = ehloLine.split(" ") try { val keyword = checkAndNormalizeEhloKeyword(parts[0]) val parameters = checkEhloParameters(parts) if (keywords.containsKey(keyword)) { parserError("Same EHLO keyword present in more than one response line") parserError("Same EHLO keyword present in more than one response line", logging = false) } keywords[keyword] = parameters } catch (e: SmtpResponseParserException) { logger.log(e, "Ignoring EHLO keyword line: $ehloLine") } } private fun checkAndNormalizeEhloKeyword(text: String): String { val keyword = text.uppercase() if (!keyword[0].isCapitalAlphaDigit() || keyword.any { !it.isCapitalAlphaDigit() && it != DASH }) { parserError("EHLO keyword contains invalid character") parserError("EHLO keyword contains invalid character", logging = false) } return keyword Loading @@ -138,9 +143,9 @@ internal class SmtpResponseParser( for (i in 1..parts.lastIndex) { val parameter = parts[i] if (parameter.isEmpty()) { parserError("EHLO parameter must not be empty") parserError("EHLO parameter must not be empty", logging = false) } else if (parameter.any { it.code !in 33..126 }) { parserError("EHLO parameter contains invalid character") parserError("EHLO parameter contains invalid character", logging = false) } } Loading
mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserTest.kt +40 −37 Original line number Diff line number Diff line Loading @@ -145,26 +145,36 @@ class SmtpResponseParserTest { } @Test fun `read EHLO response with invalid keyword`() { fun `read EHLO response with invalid keywords`() { val input = """ 250-smtp.domain.example 250 KEY:WORD 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-PIPE_CONNECT 250-AUTH=PLAIN 250 HELP """.toPeekableInputStream() val parser = SmtpResponseParser(logger, input) assertFailsWithMessage("EHLO keyword contains invalid character") { parser.readHelloResponse() val response = parser.readHelloResponse() assertType<SmtpHelloResponse.Hello>(response) { hello -> assertThat(hello.keywords.keys).containsExactly( "SIZE", "8BITMIME", "PIPELINING", "HELP" ) } assertThat(logger.logEntries).containsExactly( LogEntry( throwable = null, message = """ SMTP response data on parser error: 250-smtp.domain.example 250 KEY:WORD """.trimIndent() assertThat(logger.logEntries.map { it.message }).containsExactly( "Ignoring EHLO keyword line: PIPE_CONNECT", "Ignoring EHLO keyword line: AUTH=PLAIN" ) assertThat(logger.logEntries.map { it.throwable?.message }).containsExactly( "EHLO keyword contains invalid character", "EHLO keyword contains invalid character" ) } Loading @@ -176,44 +186,37 @@ class SmtpResponseParserTest { """.toPeekableInputStream() val parser = SmtpResponseParser(logger, input) assertFailsWithMessage("EHLO parameter must not be empty") { parser.readHelloResponse() val response = parser.readHelloResponse() assertType<SmtpHelloResponse.Hello>(response) { hello -> assertThat(hello.keywords.keys).isEmpty() } assertThat(logger.logEntries).containsExactly( LogEntry( throwable = null, message = """ SMTP response data on parser error: 250-smtp.domain.example 250 KEYWORD${" "} """.trimIndent() ) ) assertThat(logger.logEntries).hasSize(1) assertThat(logger.logEntries.first().throwable).hasMessageThat().isEqualTo("EHLO parameter must not be empty") assertThat(logger.logEntries.first().message).isEqualTo("Ignoring EHLO keyword line: KEYWORD ") } @Test fun `read EHLO response with invalid parameter`() { val input = """ 250-smtp.domain.example 250-8BITMIME 250 KEYWORD para${"\t"}meter """.toPeekableInputStream() val parser = SmtpResponseParser(logger, input) assertFailsWithMessage("EHLO parameter contains invalid character") { parser.readHelloResponse() val response = parser.readHelloResponse() assertType<SmtpHelloResponse.Hello>(response) { hello -> assertThat(hello.keywords.keys).containsExactly("8BITMIME") } assertThat(logger.logEntries).containsExactly( LogEntry( throwable = null, message = """ SMTP response data on parser error: 250-smtp.domain.example 250 KEYWORD para${"\t"}meter """.trimIndent() ) ) assertThat(logger.logEntries).hasSize(1) assertThat(logger.logEntries.first().throwable) .hasMessageThat().isEqualTo("EHLO parameter contains invalid character") assertThat(logger.logEntries.first().message) .isEqualTo("Ignoring EHLO keyword line: KEYWORD para${"\t"}meter") } @Test Loading