Extract open() method, improve Pop3Connection testing and tidy up

This commit is contained in:
Philip Whitehouse 2018-01-12 00:14:16 +00:00
parent 3cf141553e
commit 7a648cb9ab
4 changed files with 294 additions and 98 deletions

View file

@ -39,6 +39,7 @@ import static com.fsck.k9.mail.store.pop3.Pop3Commands.*;
class Pop3Connection { class Pop3Connection {
private final Pop3Settings settings; private final Pop3Settings settings;
private final TrustedSocketFactory trustedSocketFactory;
private Socket socket; private Socket socket;
private BufferedInputStream in; private BufferedInputStream in;
private BufferedOutputStream out; private BufferedOutputStream out;
@ -52,12 +53,17 @@ class Pop3Connection {
private boolean topNotAdvertised; private boolean topNotAdvertised;
Pop3Connection(Pop3Settings settings, Pop3Connection(Pop3Settings settings,
TrustedSocketFactory trustedSocketFactory) throws MessagingException { TrustedSocketFactory trustedSocketFactory) {
this.settings = settings;
this.trustedSocketFactory = trustedSocketFactory;
}
void open() throws MessagingException {
try { try {
this.settings = settings;
SocketAddress socketAddress = new InetSocketAddress(settings.getHost(), settings.getPort()); SocketAddress socketAddress = new InetSocketAddress(settings.getHost(), settings.getPort());
if (settings.getConnectionSecurity() == ConnectionSecurity.SSL_TLS_REQUIRED) { if (settings.getConnectionSecurity() == ConnectionSecurity.SSL_TLS_REQUIRED) {
socket = trustedSocketFactory.createSocket(null, settings.getHost(), settings.getPort(), settings.getClientCertificateAlias()); socket = trustedSocketFactory.createSocket(null, settings.getHost(),
settings.getPort(), settings.getClientCertificateAlias());
} else { } else {
socket = new Socket(); socket = new Socket();
} }
@ -156,7 +162,7 @@ class Pop3Connection {
default: default:
throw new MessagingException( throw new MessagingException(
"Unhandled authentication method found in the server settings (bug)."); "Unhandled authentication method: "+authType+" found in the server settings (bug).");
} }
} }

View file

@ -61,6 +61,7 @@ class Pop3Folder extends Folder<Pop3Message> {
} }
connection = pop3Store.createConnection(); connection = pop3Store.createConnection();
connection.open();
String response = connection.executeSimpleCommand(STAT_COMMAND); String response = connection.executeSimpleCommand(STAT_COMMAND);
String[] parts = response.split(" "); String[] parts = response.split(" ");

View file

@ -5,6 +5,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
@ -23,7 +24,14 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -35,135 +43,316 @@ public class Pop3ConnectionTest {
private static String password = "password"; private static String password = "password";
private static final String INITIAL_RESPONSE = "+OK POP3 server greeting\r\n"; private static final String INITIAL_RESPONSE = "+OK POP3 server greeting\r\n";
private static final String AUTH = "AUTH\r\n"; private static final String AUTH = "AUTH\r\n";
private static final String AUTH_HANDLE_RESPONSE = "+OK Listing of supported mechanisms follows\r\n" + private static final String AUTH_HANDLE_RESPONSE =
"+OK Listing of supported mechanisms follows\r\n" +
"PLAIN\r\n" + "PLAIN\r\n" +
"CRAM-MD5\r\n" + "CRAM-MD5\r\n" +
"EXTERNAL\r\n" + "EXTERNAL\r\n" +
".\r\n"; ".\r\n";
private static final String AUTH_NO_AUTH_PLAIN_RESPONSE = "+OK Listing of supported mechanisms follows\r\n" + private static final String CAPA =
"CRAM-MD5\r\n" + "CAPA\r\n";
"EXTERNAL\r\n" + private static final String CAPA_RESPONSE =
".\r\n"; "+OK Listing of supported mechanisms follows\r\n" +
private static final String CAPA = "CAPA\r\n";
private static final String CAPA_RESPONSE = "+OK Listing of supported mechanisms follows\r\n" +
"PLAIN\r\n" + "PLAIN\r\n" +
"CRAM-MD5\r\n" + "CRAM-MD5\r\n" +
"EXTERNAL\r\n" + "EXTERNAL\r\n" +
".\r\n"; ".\r\n";
private static final String CAPA_NO_AUTH_PLAIN_RESPONSE = "+OK Listing of supported mechanisms follows\r\n" +
"CRAM-MD5\r\n" +
"EXTERNAL\r\n" +
".\r\n";
private static final String LOGIN_AUTHENTICATED_RESPONSE = "+OK\r\n" + "+OK\r\n";
private static final String LOGIN = "USER "+username+"\r\n" + "PASS "+password+"\r\n";
private static final String AUTH_PLAIN_WITH_LOGIN = "AUTH PLAIN\r\n" + private static final String AUTH_PLAIN_WITH_LOGIN = "AUTH PLAIN\r\n" +
new String(Base64.encodeBase64(("\000"+username+"\000"+password).getBytes())) + "\r\n"; new String(Base64.encodeBase64(("\000"+username+"\000"+password).getBytes())) + "\r\n";
private static final String AUTH_PLAIN_AUTHENTICATED_RESPONSE = "+OK\r\n" + "+OK\r\n"; private static final String AUTH_PLAIN_AUTHENTICATED_RESPONSE = "+OK\r\n" + "+OK\r\n";
private static final String SUCCESSFUL_PLAIN_AUTH = AUTH + CAPA + AUTH_PLAIN_WITH_LOGIN;
private static final String SUCCESSFUL_PLAIN_AUTH_RESPONSE =
INITIAL_RESPONSE +
AUTH_HANDLE_RESPONSE +
CAPA_RESPONSE +
AUTH_PLAIN_AUTHENTICATED_RESPONSE;
/**
private static final String AUTH_PLAIN_FAILED_RESPONSE = "+OK\r\n" + "Plain authentication failure"; private static final String AUTH_PLAIN_FAILED_RESPONSE = "+OK\r\n" + "Plain authentication failure";
private static final String STAT = "STAT\r\n"; private static final String STAT = "STAT\r\n";
private static final String STAT_RESPONSE = "+OK 20 0\r\n"; private static final String STAT_RESPONSE = "+OK 20 0\r\n";
private static final String UIDL_UNSUPPORTED_RESPONSE = "-ERR UIDL unsupported\r\n"; private static final String UIDL_UNSUPPORTED_RESPONSE = "-ERR UIDL unsupported\r\n";
private static final String UIDL_SUPPORTED_RESPONSE = "+OK UIDL supported\r\n"; private static final String UIDL_SUPPORTED_RESPONSE = "+OK UIDL supported\r\n";
**/
private TrustedSocketFactory mockTrustedSocketFactory; private TrustedSocketFactory mockTrustedSocketFactory;
private Socket mockSocket; private Socket mockSocket;
private ByteArrayOutputStream outputStream; private ByteArrayOutputStream outputStreamForMockSocket;
private SimplePop3Settings settings; private SimplePop3Settings settings;
private TrustedSocketFactory socketFactory; private TrustedSocketFactory socketFactory;
@Before @Before
public void before() throws Exception { public void before() throws Exception {
createCommonSettings();
createMocks();
socketFactory = TestTrustedSocketFactory.newInstance();
}
private void createCommonSettings() {
settings = new SimplePop3Settings(); settings = new SimplePop3Settings();
settings.setUsername(username); settings.setUsername(username);
settings.setPassword(password); settings.setPassword(password);
socketFactory = TestTrustedSocketFactory.newInstance();
mockTrustedSocketFactory = mock(TrustedSocketFactory.class); //TODO: Remove
mockSocket = mock(Socket.class); //TODO: Remove
outputStream = new ByteArrayOutputStream(); //TODO: Remove
when(mockTrustedSocketFactory.createSocket(null, "server", 12345, null)).thenReturn(mockSocket); //TODO: Remove
when(mockSocket.getOutputStream()).thenReturn(outputStream); //TODO: Remove
when(mockSocket.isConnected()).thenReturn(true); //TODO: Remove
} }
private void setSettingsForMockSocket() { private void createMocks()
throws MessagingException, IOException, NoSuchAlgorithmException, KeyManagementException {
mockTrustedSocketFactory = mock(TrustedSocketFactory.class);
mockSocket = mock(Socket.class);
outputStreamForMockSocket = new ByteArrayOutputStream();
when(mockTrustedSocketFactory.createSocket(null, host, port, null))
.thenReturn(mockSocket);
when(mockSocket.getOutputStream()).thenReturn(outputStreamForMockSocket);
when(mockSocket.isConnected()).thenReturn(true);
}
private void addSettingsForValidMockSocket() {
settings.setHost(host); settings.setHost(host);
settings.setPort(port); settings.setPort(port);
settings.setConnectionSecurity(ConnectionSecurity.SSL_TLS_REQUIRED); settings.setConnectionSecurity(ConnectionSecurity.SSL_TLS_REQUIRED);
} }
@Test
public void constructor_doesntCreateSocket() throws Exception {
addSettingsForValidMockSocket();
settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory);
verifyZeroInteractions(mockTrustedSocketFactory);
}
//Using MockSocketFactory
@Test(expected = CertificateValidationException.class) @Test(expected = CertificateValidationException.class)
public void whenTrustedSocketFactoryThrowsSSLCertificateException_throwCertificateValidationException() throws Exception { public void open_whenTrustedSocketFactoryThrowsSSLCertificateException_throwCertificateValidationException()
when(mockTrustedSocketFactory.createSocket(null, "server", 12345, null)).thenThrow( throws Exception {
when(mockTrustedSocketFactory.createSocket(null, host, port, null)).thenThrow(
new SSLException(new CertificateException())); new SSLException(new CertificateException()));
setSettingsForMockSocket(); addSettingsForValidMockSocket();
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory); Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
} }
@Test(expected = MessagingException.class) @Test(expected = MessagingException.class)
public void whenTrustedSocketFactoryThrowsCertificateException_throwMessagingException() throws Exception { public void open_whenTrustedSocketFactoryThrowsCertificateException_throwMessagingException() throws Exception {
when(mockTrustedSocketFactory.createSocket(null, "server", 12345, null)).thenThrow( when(mockTrustedSocketFactory.createSocket(null, host, port, null)).thenThrow(
new SSLException("")); new SSLException(""));
addSettingsForValidMockSocket();
setSettingsForMockSocket();
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory); Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
} }
@Test(expected = MessagingException.class) @Test(expected = MessagingException.class)
public void whenTrustedSocketFactoryThrowsGeneralSecurityException_throwMessagingException() throws Exception { public void open_whenTrustedSocketFactoryThrowsGeneralSecurityException_throwMessagingException() throws Exception {
when(mockTrustedSocketFactory.createSocket(null, "server", 12345, null)).thenThrow( when(mockTrustedSocketFactory.createSocket(null, host, port, null)).thenThrow(
new NoSuchAlgorithmException("")); new NoSuchAlgorithmException(""));
addSettingsForValidMockSocket();
setSettingsForMockSocket();
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory); Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
} }
@Test(expected = MessagingException.class) @Test(expected = MessagingException.class)
public void whenTrustedSocketFactoryThrowsIOException_throwMessagingException() throws Exception { public void open_whenTrustedSocketFactoryThrowsIOException_throwMessagingException() throws Exception {
when(mockTrustedSocketFactory.createSocket(null, "server", 12345, null)).thenThrow( when(mockTrustedSocketFactory.createSocket(null, host, port, null)).thenThrow(
new IOException("")); new IOException(""));
addSettingsForValidMockSocket();
setSettingsForMockSocket();
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory); Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
} }
@Test(expected = MessagingException.class) @Test(expected = MessagingException.class)
public void whenSocketNotConnected_throwsMessagingException() throws Exception { public void open_whenSocketNotConnected_throwsMessagingException() throws Exception {
when(mockSocket.isConnected()).thenReturn(false); when(mockSocket.isConnected()).thenReturn(false);
addSettingsForValidMockSocket();
setSettingsForMockSocket();
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory); Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
} }
@Test @Test
public void withTLS_connectsToSocket() throws Exception { public void open_withTLS_authenticatesOverSocket() throws Exception {
String response = INITIAL_RESPONSE + when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(SUCCESSFUL_PLAIN_AUTH_RESPONSE.getBytes()));
AUTH_HANDLE_RESPONSE + addSettingsForValidMockSocket();
CAPA_RESPONSE +
AUTH_PLAIN_AUTHENTICATED_RESPONSE;
when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(response.getBytes()));
setSettingsForMockSocket();
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
new Pop3Connection(settings, mockTrustedSocketFactory); Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
assertEquals(AUTH + assertEquals(SUCCESSFUL_PLAIN_AUTH, new String(outputStreamForMockSocket.toByteArray()));
CAPA + }
AUTH_PLAIN_WITH_LOGIN, new String(outputStream.toByteArray()));
//Using both
@Test(expected = CertificateValidationException.class)
public void open_withSTLSunavailable_throwsCertificateValidationException() throws Exception {
MockPop3Server server = setupUnavailableStartTLSConnection();
settings.setAuthType(AuthType.PLAIN);
settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
createAndOpenPop3Connection(settings, mockTrustedSocketFactory);
} }
@Test @Test
public void withAuthTypePlainAndPlainAuthCapability_performsPlainAuth() throws Exception { public void open_withSTLSunavailable_doesntCreateSocket() throws Exception {
MockPop3Server server = setupUnavailableStartTLSConnection();
settings.setAuthType(AuthType.PLAIN);
settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
try {
Pop3Connection connection = new Pop3Connection(settings, mockTrustedSocketFactory);
connection.open();
} catch (Exception ignored) {
}
verify(mockTrustedSocketFactory, never()).createSocket(any(Socket.class), anyString(),
anyInt(), anyString());
}
@Test(expected = Pop3ErrorResponse.class)
public void open_withStartTLS_withSTLSerr_throwsException() throws Exception {
MockPop3Server server = setupFailedStartTLSConnection();
when(mockTrustedSocketFactory.createSocket(
any(Socket.class), eq(server.getHost()), eq(server.getPort()), eq((String) null)))
.thenReturn(mockSocket);
when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(SUCCESSFUL_PLAIN_AUTH_RESPONSE.getBytes()));
createAndOpenPop3Connection(settings, mockTrustedSocketFactory);
}
@Test
public void open_withStartTLS_withSTLSerr_doesntCreateSocket() throws Exception {
MockPop3Server server = setupFailedStartTLSConnection();;
when(mockTrustedSocketFactory.createSocket(
any(Socket.class), eq(server.getHost()), eq(server.getPort()), eq((String) null)))
.thenReturn(mockSocket);
when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(SUCCESSFUL_PLAIN_AUTH_RESPONSE.getBytes()));
try {
createAndOpenPop3Connection(settings, mockTrustedSocketFactory);
} catch (Exception ignored) {
}
verify(mockTrustedSocketFactory, never()).createSocket(any(Socket.class), anyString(),
anyInt(), anyString());
}
@Test
public void open_withStartTLS_usesSocketFactoryToCreateTLSSocket() throws Exception {
MockPop3Server server = setupStartTLSConnection();
settings.setAuthType(AuthType.PLAIN);
when(mockTrustedSocketFactory.createSocket(
any(Socket.class), eq(server.getHost()), eq(server.getPort()), eq((String) null)))
.thenReturn(mockSocket);
when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(SUCCESSFUL_PLAIN_AUTH_RESPONSE.getBytes()));
createAndOpenPop3Connection(settings, mockTrustedSocketFactory);
verify(mockTrustedSocketFactory).createSocket(any(Socket.class), eq(server.getHost()),
eq(server.getPort()), eq((String) null));
}
@Test(expected = MessagingException.class)
public void open_withStartTLS_whenSocketFactoryThrowsException_ThrowsException() throws Exception {
MockPop3Server server = setupStartTLSConnection();
settings.setAuthType(AuthType.PLAIN);
when(mockTrustedSocketFactory.createSocket(
any(Socket.class), eq(server.getHost()), eq(server.getPort()), eq((String) null)))
.thenThrow(new IOException());
when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(SUCCESSFUL_PLAIN_AUTH_RESPONSE.getBytes()));
createAndOpenPop3Connection(settings, mockTrustedSocketFactory);
verify(mockTrustedSocketFactory).createSocket(any(Socket.class), eq(server.getHost()),
eq(server.getPort()), eq((String) null));
}
@Test
public void open_withStartTLS_authenticatesOverSecureSocket() throws Exception {
MockPop3Server server = setupStartTLSConnection();
settings.setAuthType(AuthType.PLAIN);
when(mockTrustedSocketFactory.createSocket(
any(Socket.class), eq(server.getHost()), eq(server.getPort()), eq((String) null)))
.thenReturn(mockSocket);
when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(SUCCESSFUL_PLAIN_AUTH_RESPONSE.getBytes()));
createAndOpenPop3Connection(settings, mockTrustedSocketFactory);
assertEquals(SUCCESSFUL_PLAIN_AUTH, new String(outputStreamForMockSocket.toByteArray()));
}
private MockPop3Server setupStartTLSConnection() throws IOException {new MockPop3Server();
MockPop3Server server = new MockPop3Server();
setupServerWithStartTLSAvailable(server);
server.expect("STLS");
server.output("+OK Begin TLS negotiation");
server.start();
settings.setHost(server.getHost());
settings.setPort(server.getPort());
settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
return server;
}
private MockPop3Server setupFailedStartTLSConnection() throws IOException {new MockPop3Server();
MockPop3Server server = new MockPop3Server();
setupServerWithStartTLSAvailable(server);
server.expect("STLS");
server.output("-ERR Unavailable");
server.start();
settings.setHost(server.getHost());
settings.setPort(server.getPort());
settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
return server;
}
private MockPop3Server setupUnavailableStartTLSConnection() throws IOException {new MockPop3Server();
MockPop3Server server = new MockPop3Server();
server.output("+OK POP3 server greeting");
server.expect("AUTH");
server.output("+OK Listing of supported mechanisms follows");
server.output("PLAIN");
server.output(".");
server.expect("CAPA");
server.output("+OK Listing of supported mechanisms follows");
server.output(".");
server.start();
settings.setHost(server.getHost());
settings.setPort(server.getPort());
settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
return server;
}
private void setupServerWithStartTLSAvailable(MockPop3Server server) {
server.output("+OK POP3 server greeting");
server.expect("AUTH");
server.output("+OK Listing of supported mechanisms follows");
server.output("PLAIN");
server.output(".");
server.expect("CAPA");
server.output("+OK Listing of supported mechanisms follows");
server.output("STLS");
server.output(".");
}
//Using RealSocketFactory with MockPop3Server
@Test
public void open_withAuthTypePlainAndPlainAuthCapability_performsPlainAuth() throws Exception {
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -184,14 +373,14 @@ public class Pop3ConnectionTest {
server.output("+OK"); server.output("+OK");
server.expect(new String(Base64.encodeBase64(("\000"+username+"\000"+password).getBytes()))); server.expect(new String(Base64.encodeBase64(("\000"+username+"\000"+password).getBytes())));
server.output("+OK"); server.output("+OK");
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
server.verifyConnectionStillOpen(); server.verifyConnectionStillOpen();
server.verifyInteractionCompleted(); server.verifyInteractionCompleted();
} }
@Test @Test
public void withAuthTypePlainAndPlainAuthCapabilityAndInvalidPasswordResponse_throwsException() throws Exception { public void open_withAuthTypePlainAndPlainAuthCapabilityAndInvalidPasswordResponse_throwsException() throws Exception {
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -214,7 +403,7 @@ public class Pop3ConnectionTest {
server.output("-ERR"); server.output("-ERR");
try { try {
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
fail("Expected auth failure"); fail("Expected auth failure");
} catch (AuthenticationFailedException ignored) {} } catch (AuthenticationFailedException ignored) {}
@ -222,7 +411,7 @@ public class Pop3ConnectionTest {
} }
@Test @Test
public void withAuthTypePlainAndNoPlainAuthCapability_performsLogin() throws Exception { public void open_withAuthTypePlainAndNoPlainAuthCapability_performsLogin() throws Exception {
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -243,7 +432,7 @@ public class Pop3ConnectionTest {
server.output("-ERR"); server.output("-ERR");
try { try {
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
fail("Expected auth failure"); fail("Expected auth failure");
} catch (AuthenticationFailedException ignored) {} } catch (AuthenticationFailedException ignored) {}
@ -251,7 +440,7 @@ public class Pop3ConnectionTest {
} }
@Test @Test
public void withAuthTypePlainAndNoPlainAuthCapabilityAndLoginFailure_throwsException() throws Exception { public void open_withAuthTypePlainAndNoPlainAuthCapabilityAndLoginFailure_throwsException() throws Exception {
settings.setAuthType(AuthType.PLAIN); settings.setAuthType(AuthType.PLAIN);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -271,16 +460,15 @@ public class Pop3ConnectionTest {
server.expect("PASS password"); server.expect("PASS password");
server.output("+OK"); server.output("+OK");
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
server.verifyInteractionCompleted(); server.verifyInteractionCompleted();
} }
@Test @Test
public void withAuthTypeCramMd5AndCapability_performsCramMd5Auth() throws IOException, MessagingException { public void open_withAuthTypeCramMd5AndCapability_performsCramMd5Auth() throws IOException, MessagingException {
settings.setAuthType(AuthType.CRAM_MD5); settings.setAuthType(AuthType.CRAM_MD5);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
server.output("+OK POP3 server greeting"); server.output("+OK POP3 server greeting");
server.expect("AUTH"); server.expect("AUTH");
@ -299,14 +487,14 @@ public class Pop3ConnectionTest {
server.output("+ abcd"); server.output("+ abcd");
server.expect("dXNlciBhZGFhZTU2Zjk1NzAxZjQwNDQwZjhhMWU2YzY1ZjZmZg=="); server.expect("dXNlciBhZGFhZTU2Zjk1NzAxZjQwNDQwZjhhMWU2YzY1ZjZmZg==");
server.output("+OK"); server.output("+OK");
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
server.verifyConnectionStillOpen(); server.verifyConnectionStillOpen();
server.verifyInteractionCompleted(); server.verifyInteractionCompleted();
} }
@Test @Test
public void withAuthTypeCramMd5AndCapabilityAndCramFailure_throwsException() throws IOException, MessagingException { public void open_withAuthTypeCramMd5AndCapabilityAndCramFailure_throwsException() throws IOException, MessagingException {
settings.setAuthType(AuthType.CRAM_MD5); settings.setAuthType(AuthType.CRAM_MD5);
@ -330,7 +518,7 @@ public class Pop3ConnectionTest {
server.output("-ERR"); server.output("-ERR");
try { try {
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
fail("Expected auth failure"); fail("Expected auth failure");
} catch (AuthenticationFailedException ignored) {} } catch (AuthenticationFailedException ignored) {}
@ -338,7 +526,7 @@ public class Pop3ConnectionTest {
} }
@Test @Test
public void withAuthTypeCramMd5AndNoCapability_performsApopAuth() throws IOException, MessagingException { public void open_withAuthTypeCramMd5AndNoCapability_performsApopAuth() throws IOException, MessagingException {
settings.setAuthType(AuthType.CRAM_MD5); settings.setAuthType(AuthType.CRAM_MD5);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -355,14 +543,14 @@ public class Pop3ConnectionTest {
server.output("."); server.output(".");
server.expect("APOP user c8e8c560e385faaa6367d4145572b8ea"); server.expect("APOP user c8e8c560e385faaa6367d4145572b8ea");
server.output("+OK"); server.output("+OK");
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
server.verifyConnectionStillOpen(); server.verifyConnectionStillOpen();
server.verifyInteractionCompleted(); server.verifyInteractionCompleted();
} }
@Test @Test
public void withAuthTypeCramMd5AndNoCapabilityAndApopFailure_throwsException() throws IOException, MessagingException { public void open_withAuthTypeCramMd5AndNoCapabilityAndApopFailure_throwsException() throws IOException, MessagingException {
settings.setAuthType(AuthType.CRAM_MD5); settings.setAuthType(AuthType.CRAM_MD5);
@ -382,7 +570,7 @@ public class Pop3ConnectionTest {
server.output("-ERR"); server.output("-ERR");
try { try {
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
fail("Expected auth failure"); fail("Expected auth failure");
} catch (AuthenticationFailedException ignored) {} } catch (AuthenticationFailedException ignored) {}
@ -390,7 +578,7 @@ public class Pop3ConnectionTest {
} }
@Test @Test
public void withAuthTypeExternalAndCapability_performsExternalAuth() throws IOException, MessagingException { public void open_withAuthTypeExternalAndCapability_performsExternalAuth() throws IOException, MessagingException {
settings.setAuthType(AuthType.EXTERNAL); settings.setAuthType(AuthType.EXTERNAL);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -409,14 +597,14 @@ public class Pop3ConnectionTest {
server.output("."); server.output(".");
server.expect("AUTH EXTERNAL dXNlcg=="); server.expect("AUTH EXTERNAL dXNlcg==");
server.output("+OK"); server.output("+OK");
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
server.verifyConnectionStillOpen(); server.verifyConnectionStillOpen();
server.verifyInteractionCompleted(); server.verifyInteractionCompleted();
} }
@Test @Test
public void withAuthTypeExternalAndNoCapability_throwsCVE() throws IOException, MessagingException { public void open_withAuthTypeExternalAndNoCapability_throwsCVE() throws IOException, MessagingException {
settings.setAuthType(AuthType.EXTERNAL); settings.setAuthType(AuthType.EXTERNAL);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -434,7 +622,7 @@ public class Pop3ConnectionTest {
server.output("."); server.output(".");
try { try {
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
fail("CVE expected"); fail("CVE expected");
} catch (CertificateValidationException e) { } catch (CertificateValidationException e) {
assertEquals(Reason.MissingCapability, e.getReason()); assertEquals(Reason.MissingCapability, e.getReason());
@ -445,7 +633,7 @@ public class Pop3ConnectionTest {
} }
@Test @Test
public void withAuthTypeExternalAndCapability_withRejection_throwsCVE() throws IOException, MessagingException { public void open_withAuthTypeExternalAndCapability_withRejection_throwsCVE() throws IOException, MessagingException {
settings.setAuthType(AuthType.EXTERNAL); settings.setAuthType(AuthType.EXTERNAL);
MockPop3Server server = new MockPop3Server(); MockPop3Server server = new MockPop3Server();
@ -466,7 +654,7 @@ public class Pop3ConnectionTest {
server.output("-ERR Invalid certificate"); server.output("-ERR Invalid certificate");
try { try {
startServerAndCreateConnection(server); startServerAndCreateOpenConnection(server);
fail("CVE expected"); fail("CVE expected");
} catch (CertificateValidationException e) { } catch (CertificateValidationException e) {
assertEquals("POP3 client certificate authentication failed: -ERR Invalid certificate", e.getMessage()); assertEquals("POP3 client certificate authentication failed: -ERR Invalid certificate", e.getMessage());
@ -475,16 +663,17 @@ public class Pop3ConnectionTest {
server.verifyInteractionCompleted(); server.verifyInteractionCompleted();
} }
private Pop3Connection startServerAndCreateConnection(MockPop3Server server) throws IOException, private void startServerAndCreateOpenConnection(MockPop3Server server) throws IOException,
MessagingException { MessagingException {
server.start(); server.start();
settings.setHost(server.getHost()); settings.setHost(server.getHost());
settings.setPort(server.getPort()); settings.setPort(server.getPort());
return createPop3Connection(settings, socketFactory); createAndOpenPop3Connection(settings, socketFactory);
} }
private Pop3Connection createPop3Connection(Pop3Settings settings, TrustedSocketFactory socketFactory) private void createAndOpenPop3Connection(Pop3Settings settings, TrustedSocketFactory socketFactory)
throws MessagingException { throws MessagingException {
return new Pop3Connection(settings, socketFactory); Pop3Connection connection = new Pop3Connection(settings, socketFactory);
connection.open();
} }
} }

View file

@ -53,16 +53,7 @@ public class Pop3FolderTest {
when(mockStore.createConnection()).thenReturn(mockConnection); when(mockStore.createConnection()).thenReturn(mockConnection);
when(mockConnection.executeSimpleCommand(Pop3Commands.STAT_COMMAND)).thenReturn("+OK 10 0"); when(mockConnection.executeSimpleCommand(Pop3Commands.STAT_COMMAND)).thenReturn("+OK 10 0");
folder = new Pop3Folder(mockStore, "Inbox"); folder = new Pop3Folder(mockStore, "Inbox");
setupTempDirectory(); BinaryTempFileBody.setTempDirectory(new File(System.getProperty("java.io.tmpdir")));
}
private void setupTempDirectory() {
File tempDirectory = new File("temp");
if (!tempDirectory.exists()) {
assertTrue(tempDirectory.mkdir());
tempDirectory.deleteOnExit();
}
BinaryTempFileBody.setTempDirectory(tempDirectory);
} }
@Test @Test
@ -158,6 +149,15 @@ public class Pop3FolderTest {
folder.open(Folder.OPEN_MODE_RW); folder.open(Folder.OPEN_MODE_RW);
} }
@Test
public void open_createsAndOpensConnection()
throws MessagingException {
folder.open(Folder.OPEN_MODE_RW);
verify(mockStore, times(1)).createConnection();
verify(mockConnection).open();
}
@Test @Test
public void open_whenAlreadyOpenWithValidConnection_doesNotCreateAnotherConnection() public void open_whenAlreadyOpenWithValidConnection_doesNotCreateAnotherConnection()
throws MessagingException { throws MessagingException {