SMTP: Further test coverage for various cases

This commit is contained in:
Philip Whitehouse 2016-09-30 18:00:27 +02:00 committed by cketti
parent d1904900c8
commit a2b545fc39
4 changed files with 309 additions and 9 deletions

View file

@ -532,10 +532,8 @@ public class SmtpTransport extends Transport {
entireMessageSent = true; // After the "\r\n." is attempted, we may have sent the message
executeSimpleCommand(".");
} catch (NegativeSmtpReplyException e) {
e.printStackTrace();
throw e;
} catch (Exception e) {
e.printStackTrace();
MessagingException me = new MessagingException("Unable to send message", e);
me.setPermanentFailure(entireMessageSent);

View file

@ -2,6 +2,8 @@ package com.fsck.k9.mail.transport;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
@ -12,6 +14,7 @@ import com.fsck.k9.mail.ssl.TrustedSocketFactory;
import com.fsck.k9.mail.store.StoreConfig;
import com.fsck.k9.mail.transport.mockServer.MockSmtpServer;
import com.fsck.k9.mail.transport.mockServer.TestMessage;
import com.fsck.k9.mailstore.BinaryMemoryBody;
import com.fsck.k9.testHelpers.TestTrustedSocketFactory;
import org.junit.Before;
@ -24,6 +27,12 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -86,7 +95,6 @@ public class SmtpTransportTest {
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250-SIZE 1000000");
for (String extension: extensions) {
server.output("250-"+extension);
}
@ -115,6 +123,26 @@ public class SmtpTransportTest {
new SmtpTransport(storeConfig, trustedSocketFactory);
}
@Test
public void open_withNoSecurityOrPasswordPlainAuth_connectsToServer_withoutLogin()
throws MessagingException, IOException, InterruptedException {
username = "user";
authenticationType = AuthType.PLAIN;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 OK");
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityPlainAuth_connectsToServer()
throws MessagingException, IOException, InterruptedException {
@ -127,11 +155,10 @@ public class SmtpTransportTest {
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250-SIZE 1000000");
server.output("250 AUTH LOGIN PLAIN CRAM-MD5");
server.output("250 AUTH PLAIN LOGIN");
server.expect("AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=");
server.output("235 2.7.0 Authentication successful");
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
@ -139,6 +166,60 @@ public class SmtpTransportTest {
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityPlainAuth_usesLoginIfPlainUnavailable()
throws MessagingException, IOException, InterruptedException {
username = "user";
password = "password";
authenticationType = AuthType.PLAIN;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 AUTH LOGIN");
server.expect("AUTH LOGIN");
server.output("250 OK");
server.expect("dXNlcg==");
server.output("250 OK");
server.expect("cGFzc3dvcmQ=");
server.output("235 2.7.0 Authentication successful");
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityPlainAuth_withNeither_throwsException()
throws MessagingException, IOException, InterruptedException {
username = "user";
password = "password";
authenticationType = AuthType.PLAIN;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 AUTH");
try {
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
fail("Exception expected");
} catch (MessagingException e) {
assertEquals("Authentication methods SASL PLAIN and LOGIN are unavailable.",
e.getMessage());
}
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityCramMd5Auth_connectsToServer()
throws MessagingException, IOException, InterruptedException {
@ -151,8 +232,7 @@ public class SmtpTransportTest {
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250-SIZE 1000000");
server.output("250 AUTH LOGIN PLAIN CRAM-MD5");
server.output("250 AUTH CRAM-MD5");
server.expect("AUTH CRAM-MD5");
server.output(Base64.encode("<24609.1047914046@localhost>"));
server.expect("dXNlciA3NmYxNWEzZmYwYTNiOGI1NzcxZmNhODZlNTcyMDk2Zg==");
@ -165,6 +245,33 @@ public class SmtpTransportTest {
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityCramMd5Auth_withNoSupport_throwsException()
throws MessagingException, IOException, InterruptedException {
username = "user";
password = "password";
authenticationType = AuthType.CRAM_MD5;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 AUTH PLAIN LOGIN");
try {
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
fail("Exception expected");
} catch (MessagingException e) {
assertEquals("Authentication method CRAM-MD5 is unavailable.",
e.getMessage());
}
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityExternalAuth_connectsToServer()
throws MessagingException, IOException, InterruptedException {
@ -177,7 +284,6 @@ public class SmtpTransportTest {
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250-SIZE 1000000");
server.output("250 AUTH EXTERNAL");
server.expect("AUTH EXTERNAL dXNlcg==");
server.output("235 2.7.0 Authentication successful");
@ -189,6 +295,104 @@ public class SmtpTransportTest {
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityExternal_withNoSupport_throwsException()
throws MessagingException, IOException, InterruptedException {
username = "user";
password = "password";
authenticationType = AuthType.EXTERNAL;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 AUTH");
try {
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
fail("Exception expected");
} catch (CertificateValidationException e) {
assertEquals(CertificateValidationException.Reason.MissingCapability, e.getReason());
}
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityAutomatic_connectsToServerWithCramMD5IfSupported()
throws MessagingException, IOException, InterruptedException {
username = "user";
password = "password";
authenticationType = AuthType.AUTOMATIC;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 AUTH CRAM-MD5");
server.expect("AUTH CRAM-MD5");
server.output(Base64.encode("<24609.1047914046@localhost>"));
server.expect("dXNlciA3NmYxNWEzZmYwYTNiOGI1NzcxZmNhODZlNTcyMDk2Zg==");
server.output("235 2.7.0 Authentication successful");
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_withNoSecurityAutomatic_withCramMD5Unsupported_throwsException()
throws MessagingException, IOException, InterruptedException {
username = "user";
password = "password";
authenticationType = AuthType.AUTOMATIC;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("250-localhost Hello client.localhost");
server.output("250 AUTH PLAIN LOGIN");
try {
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
fail("Exception expected");
} catch (MessagingException e) {
assertEquals("Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable.",
e.getMessage());
}
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void open_triesHELO_whenServerDoesntSupportEHLO()
throws MessagingException, IOException, InterruptedException {
username = "user";
authenticationType = AuthType.PLAIN;
connectionSecurity = ConnectionSecurity.NONE;
MockSmtpServer server = new MockSmtpServer();
server.output("220 localhost Simple Mail Transfer Service Ready");
server.expect("EHLO localhost");
server.output("502 5.5.1, Unrecognized command.");
server.expect("HELO localhost");
server.output("250 localhost");
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.open();
server.verifyConnectionStillOpen();
server.verifyInteractionCompleted();
}
@Test
public void sendMessage_withNoAddressToSendTo_doesntOpenConnection()
throws MessagingException, IOException, InterruptedException {
@ -257,4 +461,61 @@ public class SmtpTransportTest {
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
transport.sendMessage(message);
}
@Test
public void sendMessage_withMessageTooLarge_throwsException()
throws MessagingException, IOException, InterruptedException {
extensions.add("SIZE 1000");
TestMessage message = new TestMessage();
message.setFrom(new Address("user@localhost"));
message.setRecipients(Message.RecipientType.TO, new Address[]{new Address("user2@localhost")});
message.setAttachmentCount(1);
message.setBody(new BinaryMemoryBody(new byte[1001], "US-ASCII"));
MockSmtpServer server = new MockSmtpServer();
setupConnectAndPlainAuthentication(server);
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
try {
transport.sendMessage(message);
fail("Expected message too large error");
} catch (MessagingException e) {
assertTrue(e.isPermanentFailure());
assertEquals("Message too large for server", e.getMessage());
}
}
@Test
public void sendMessage_withNegativeReply_throwsException()
throws MessagingException, IOException, InterruptedException {
TestMessage message = new TestMessage();
message.setFrom(new Address("user@localhost"));
message.setRecipients(Message.RecipientType.TO, new Address[]{new Address("user2@localhost")});
MockSmtpServer server = new MockSmtpServer();
setupConnectAndPlainAuthentication(server);
server.expect("MAIL FROM:<user@localhost>");
server.output("250 OK");
server.expect("RCPT TO:<user2@localhost>");
server.output("250 OK");
server.expect("DATA");
server.output("354 End data with <CR><LF>.<CR><LF>");
server.expect("");
server.expect(".");
server.output("421 4.7.0 Temporary system problem");
server.expect("QUIT");
server.output("221 BYE");
server.closeConnection();
SmtpTransport transport = startServerAndCreateSmtpTransport(server);
try {
transport.sendMessage(message);
fail("Expected exception");
} catch (SmtpTransport.NegativeSmtpReplyException e) {
assertEquals(421, e.getReplyCode());
assertEquals("4.7.0 Temporary system problem", e.getReplyText());
}
}
}

View file

@ -1,5 +1,7 @@
package com.fsck.k9.mail.transport;
import android.annotation.SuppressLint;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings;
@ -8,6 +10,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
@SuppressLint("AuthLeak")
public class SmtpTransportUriTest {
@Test
@ -37,6 +40,33 @@ public class SmtpTransportUriTest {
assertEquals("password", result.password);
}
@Test
public void decodeUri_canDecodeUsername_withNoAuthType() {
String storeUri = "smtp://user:password@server:123456";
ServerSettings result = SmtpTransport.decodeUri(storeUri);
assertEquals("user", result.username);
}
@Test
public void decodeUri_canDecodeUsername_withNoPasswordOrAuthType() {
String storeUri = "smtp://user@server:123456";
ServerSettings result = SmtpTransport.decodeUri(storeUri);
assertEquals("user", result.username);
}
@Test
public void decodeUri_canDecodeAuthType_withEmptyPassword() {
String storeUri = "smtp://user::PLAIN@server:123456";
ServerSettings result = SmtpTransport.decodeUri(storeUri);
assertEquals(AuthType.PLAIN, result.authenticationType);
}
@Test
public void decodeUri_canDecodeHost() {
String storeUri = "smtp://user:password:PLAIN@server:123456";
@ -82,6 +112,13 @@ public class SmtpTransportUriTest {
assertEquals("clientCert", result.clientCertificateAlias);
}
@Test(expected = IllegalArgumentException.class)
public void decodeUri_forUnknownSchema_throwsIllegalArgumentException() {
String storeUri = "unknown://user:clientCert:EXTERNAL@server:123456";
ServerSettings result = SmtpTransport.decodeUri(storeUri);
}
@Test
public void createUri_canEncodeSmtpSslUri() {
ServerSettings serverSettings = new ServerSettings(

View file

@ -59,6 +59,10 @@ public class TestMessage extends MimeMessage {
return (mAttachmentCount > 0);
}
public void setAttachmentCount(int i) {
mAttachmentCount = i;
}
public int getAttachmentCount() {
return mAttachmentCount;
}