Merge branch 'master' into pgp_mime_preparations

Conflicts:
	k9mail/src/androidTest/java/com/fsck/k9/mailstore/LocalMessageTest.java
This commit is contained in:
cketti 2015-01-20 18:35:07 +01:00
commit 23c9398c03
28 changed files with 755 additions and 256 deletions

View File

@ -20,11 +20,12 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 17 targetSdkVersion 21
} }
lintOptions { lintOptions {
abortOnError false abortOnError true
warningsAsErrors true
lintConfig file("$rootProject.projectDir/config/lint/lint.xml") lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
} }

View File

@ -555,14 +555,14 @@ class ImapConnection {
private static Socket connect(ImapSettings settings, TrustedSocketFactory socketFactory) private static Socket connect(ImapSettings settings, TrustedSocketFactory socketFactory)
throws GeneralSecurityException, MessagingException, IOException { throws GeneralSecurityException, MessagingException, IOException {
// Try all IPv4 and IPv6 addresses of the host // Try all IPv4 and IPv6 addresses of the host
InetAddress[] addresses = InetAddress.getAllByName(settings.getHost()); Exception connectException = null;
for (int i = 0; i < addresses.length; i++) { for (InetAddress address : InetAddress.getAllByName(settings.getHost())) {
try { try {
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) { if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) {
Log.d(LOG_TAG, "Connecting to " + settings.getHost() + " as " + addresses[i]); Log.d(LOG_TAG, "Connecting to " + settings.getHost() + " as " + address);
} }
SocketAddress socketAddress = new InetSocketAddress(addresses[i], settings.getPort()); SocketAddress socketAddress = new InetSocketAddress(address, settings.getPort());
Socket socket; Socket socket;
if (settings.getConnectionSecurity() == ConnectionSecurity.SSL_TLS_REQUIRED) { if (settings.getConnectionSecurity() == ConnectionSecurity.SSL_TLS_REQUIRED) {
socket = socketFactory.createSocket( socket = socketFactory.createSocket(
@ -576,15 +576,12 @@ class ImapConnection {
socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
// Successfully connected to the server; don't try any other addresses // Successfully connected to the server; don't try any other addresses
return socket; return socket;
} catch (SocketException e) { } catch (IOException e) {
if (i < (addresses.length - 1)) { Log.w(LOG_TAG, "could not connect to "+address, e);
// There are still other addresses for that host to try connectException = e;
continue;
}
throw new MessagingException("Cannot connect to host", e);
} }
} }
throw new MessagingException("Cannot connect to host"); throw new MessagingException("Cannot connect to host", connectException);
} }
private void adjustDNSCacheTTL() { private void adjustDNSCacheTTL() {
@ -601,10 +598,18 @@ class ImapConnection {
} }
private List<ImapResponse> receiveCapabilities(List<ImapResponse> responses) { private List<ImapResponse> receiveCapabilities(List<ImapResponse> responses) {
capabilities = ImapResponseParser.parseCapabilities(responses); Set<String> receivedCapabilities = ImapResponseParser.parseCapabilities(responses);
/* RFC 3501 6.2.3
A server MAY include a CAPABILITY response code in the tagged OK
response to a successful LOGIN command in order to send
capabilities automatically. It is unnecessary for a client to
send a separate CAPABILITY command if it recognizes these
automatic capabilities.
*/
if (K9MailLib.isDebug()) { if (K9MailLib.isDebug()) {
Log.d(LOG_TAG, "Saving " + capabilities + " capabilities for " + getLogId()); Log.d(LOG_TAG, "Saving " + receivedCapabilities + " capabilities for " + getLogId());
} }
capabilities.addAll(receivedCapabilities);
return responses; return responses;
} }
} }

View File

@ -1,6 +1,7 @@
package com.fsck.k9.mail.store.pop3; package com.fsck.k9.mail.store.pop3;
import android.annotation.SuppressLint;
import android.util.Log; import android.util.Log;
import com.fsck.k9.mail.*; import com.fsck.k9.mail.*;
@ -278,6 +279,7 @@ public class Pop3Store extends RemoteStore {
private InputStream mIn; private InputStream mIn;
private OutputStream mOut; private OutputStream mOut;
private Map<String, Pop3Message> mUidToMsgMap = new HashMap<String, Pop3Message>(); private Map<String, Pop3Message> mUidToMsgMap = new HashMap<String, Pop3Message>();
@SuppressLint("UseSparseArrays")
private Map<Integer, Pop3Message> mMsgNumToMsgMap = new HashMap<Integer, Pop3Message>(); private Map<Integer, Pop3Message> mMsgNumToMsgMap = new HashMap<Integer, Pop3Message>();
private Map<String, Integer> mUidToMsgNumMap = new HashMap<String, Integer>(); private Map<String, Integer> mUidToMsgNumMap = new HashMap<String, Integer>();
private String mName; private String mName;

View File

@ -9,10 +9,8 @@ import com.fsck.k9.mail.ssl.TrustManagerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
@ -32,7 +30,7 @@ public class WebDavSocketFactory implements LayeredSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS"); SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { sslContext.init(null, new TrustManager[] {
TrustManagerFactory.get(host, port) TrustManagerFactory.get(host, port)
}, new SecureRandom()); }, null);
mSocketFactory = sslContext.getSocketFactory(); mSocketFactory = sslContext.getSocketFactory();
mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
mSchemeSocketFactory.setHostnameVerifier( mSchemeSocketFactory.setHostnameVerifier(
@ -41,7 +39,7 @@ public class WebDavSocketFactory implements LayeredSocketFactory {
public Socket connectSocket(Socket sock, String host, int port, public Socket connectSocket(Socket sock, String host, int port,
InetAddress localAddress, int localPort, HttpParams params) InetAddress localAddress, int localPort, HttpParams params)
throws IOException, UnknownHostException, ConnectTimeoutException { throws IOException, ConnectTimeoutException {
return mSchemeSocketFactory.connectSocket(sock, host, port, localAddress, localPort, params); return mSchemeSocketFactory.connectSocket(sock, host, port, localAddress, localPort, params);
} }
@ -57,7 +55,7 @@ public class WebDavSocketFactory implements LayeredSocketFactory {
final String host, final String host,
final int port, final int port,
final boolean autoClose final boolean autoClose
) throws IOException, UnknownHostException { ) throws IOException {
SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket( SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket(
socket, socket,
host, host,

View File

@ -5,6 +5,9 @@ apply from: '../gradle/plugins/findbugs-android.gradle'
repositories { repositories {
jcenter() jcenter()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
} }
dependencies { dependencies {
@ -21,16 +24,10 @@ dependencies {
androidTestCompile 'com.android.support.test:testing-support-lib:0.1' androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
androidTestCompile('com.icegreen:greenmail:1.4.1-SNAPSHOT') {
androidTestCompile("com.icegreen:greenmail:1.3.1b") { exclude group: 'junit'
// Use a better, later version
exclude group: "javax.mail"
} }
androidTestCompile 'com.madgag.spongycastle:pg:1.51.0.0'
// this version avoids some "Ignoring InnerClasses attribute for an anonymous inner class" warnings
androidTestCompile "javax.mail:javax.mail-api:1.5.2"
androidTestCompile "com.madgag.spongycastle:pg:1.51.0.0"
} }
android { android {

View File

@ -1,21 +0,0 @@
package com.fsck.k9.activity;
import android.test.ActivityInstrumentationTestCase2;
import com.fsck.k9.activity.Accounts;
/**
* This is a simple framework for a test of an Application. See
* {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on
* how to write and extend Application tests.
* <p/>
* To run this test, you can type:
* adb shell am instrument -w \
* -e class com.fsck.k9.activity.AccountsTest \
* com.fsck.k9.tests/android.test.InstrumentationTestRunner
*/
public class AccountsTest extends ActivityInstrumentationTestCase2<Accounts> {
public AccountsTest() {
super("com.fsck.k9", Accounts.class);
}
}

View File

@ -1,14 +1,23 @@
package com.fsck.k9.activity; package com.fsck.k9.activity;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import junit.framework.TestCase; import org.junit.Test;
import org.junit.runner.RunWith;
public class MessageReferenceTest extends TestCase import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@RunWith(AndroidJUnit4.class)
public class MessageReferenceTest
{ {
/** /**
* Typically happens during forwards. (You have a reference, but no flag since we don't currently consider FORWARDED a flag.) * Typically happens during forwards. (You have a reference, but no flag since we don't currently consider FORWARDED a flag.)
*/ */
@Test
public void testIdentityStringNoFlag() public void testIdentityStringNoFlag()
{ {
MessageReference mr = new MessageReference(); MessageReference mr = new MessageReference();
@ -22,6 +31,7 @@ public class MessageReferenceTest extends TestCase
/** /**
* Typically happens during replies. * Typically happens during replies.
*/ */
@Test
public void testIdentityString() public void testIdentityString()
{ {
MessageReference mr = new MessageReference(); MessageReference mr = new MessageReference();
@ -33,6 +43,7 @@ public class MessageReferenceTest extends TestCase
assertEquals("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED", mr.toIdentityString()); assertEquals("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED", mr.toIdentityString());
} }
@Test
public void testParseIdentityStringNoFlag() throws MessagingException public void testParseIdentityStringNoFlag() throws MessagingException
{ {
MessageReference mr = new MessageReference("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA="); MessageReference mr = new MessageReference("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=");
@ -42,6 +53,7 @@ public class MessageReferenceTest extends TestCase
assertNull(mr.flag); assertNull(mr.flag);
} }
@Test
public void testParseIdentityString() throws MessagingException public void testParseIdentityString() throws MessagingException
{ {
MessageReference mr = new MessageReference("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED"); MessageReference mr = new MessageReference("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED");
@ -51,24 +63,20 @@ public class MessageReferenceTest extends TestCase
assertEquals(Flag.ANSWERED, mr.flag); assertEquals(Flag.ANSWERED, mr.flag);
} }
@Test
public void testBadVersion() throws MessagingException public void testBadVersion() throws MessagingException
{ {
MessageReference mr = new MessageReference("@:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED"); MessageReference mr = new MessageReference("@:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED");
assertNull(mr.accountUuid); assertNull(mr.accountUuid);
} }
@Test(expected = MessagingException.class)
public void testNull() throws MessagingException public void testNull() throws MessagingException
{ {
try new MessageReference(null);
{
new MessageReference(null);
assertTrue(false);
} catch (MessagingException e)
{
assertTrue(true);
}
} }
@Test(expected = MessagingException.class)
public void testCorruption() throws MessagingException public void testCorruption() throws MessagingException
{ {
MessageReference mr = new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWERED"); MessageReference mr = new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWERED");
@ -78,13 +86,6 @@ public class MessageReferenceTest extends TestCase
assertNotNull(mr.uid); assertNotNull(mr.uid);
// Corruption in the Flag should throw MessagingException. // Corruption in the Flag should throw MessagingException.
try new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWE!RED");
{
new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWE!RED");
assertTrue(false);
} catch (MessagingException e)
{
assertTrue(true);
}
} }
} }

View File

@ -2,6 +2,8 @@ package com.fsck.k9.endtoend;
import com.fsck.k9.activity.setup.WelcomeMessage; import com.fsck.k9.activity.setup.WelcomeMessage;
import com.fsck.k9.endtoend.pages.WelcomeMessagePage; import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
import org.junit.Test;
/** /**
* Creates a new IMAP account via the getting started flow. * Creates a new IMAP account via the getting started flow.
@ -12,13 +14,14 @@ public class A000_WelcomeAndSetupAccountIntegrationTest extends AbstractEndToEnd
super(WelcomeMessage.class, false); super(WelcomeMessage.class, false);
} }
public void testCreateAccount() throws Exception { @Test
new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage()); public void createAccount() throws Exception {
new AccountSetupFlow().setupAccountFromWelcomePage(new WelcomeMessagePage());
} }
public void testCreateSecondAccount() throws Exception { @Test
new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage()); public void createSecondAccount() throws Exception {
new AccountSetupFlow().setupAccountFromWelcomePage(new WelcomeMessagePage());
} }
} }

View File

@ -4,6 +4,8 @@ import com.fsck.k9.activity.Accounts;
import com.fsck.k9.endtoend.framework.AccountForTest; import com.fsck.k9.endtoend.framework.AccountForTest;
import com.fsck.k9.endtoend.framework.ApplicationState; import com.fsck.k9.endtoend.framework.ApplicationState;
import com.fsck.k9.endtoend.pages.AccountsPage; import com.fsck.k9.endtoend.pages.AccountsPage;
import org.junit.Test;
/** /**
* Creates and removes accounts. * Creates and removes accounts.
@ -17,14 +19,19 @@ public class A010_AccountIntegrationTest extends AbstractEndToEndTest<Accounts>{
super(Accounts.class); super(Accounts.class);
} }
public void testCreateAccountDirectly() throws Exception { @Test
new AccountSetupFlow(this).setupAccountFromAccountsPage(new AccountsPage()); public void createAccountDirectly() throws Exception {
new AccountSetupFlow().setupAccountFromAccountsPage(new AccountsPage());
} }
public void testDeleteAccount() { @Test
public void deleteAccount() {
AccountsPage accountsPage = new AccountsPage(); AccountsPage accountsPage = new AccountsPage();
// TODO should not have cross-test-dependencies
assertFalse("NB: this test is order dependent and requires A000_WelcomeAndSetupAccountIntegrationTest to run first",
ApplicationState.getInstance().accounts.isEmpty());
AccountForTest accountForTest = ApplicationState.getInstance().accounts.get(0); AccountForTest accountForTest = ApplicationState.getInstance().accounts.get(0);
accountsPage.assertAccountExists(accountForTest.description); accountsPage.assertAccountExists(accountForTest.description);

View File

@ -1,6 +1,9 @@
package com.fsck.k9.endtoend; package com.fsck.k9.endtoend;
import android.app.Activity; import android.app.Activity;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.assertion.ViewAssertions;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2; import android.test.ActivityInstrumentationTestCase2;
import android.util.Log; import android.util.Log;
@ -8,16 +11,18 @@ import com.fsck.k9.R;
import com.fsck.k9.endtoend.framework.ApplicationState; import com.fsck.k9.endtoend.framework.ApplicationState;
import com.fsck.k9.endtoend.framework.StubMailServer; import com.fsck.k9.endtoend.framework.StubMailServer;
import com.fsck.k9.endtoend.pages.WelcomeMessagePage; import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
import android.support.test.espresso.assertion.ViewAssertions;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withId;
@RunWith(AndroidJUnit4.class)
public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> { public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
private ApplicationState state = ApplicationState.getInstance();
private final boolean bypassWelcome; private final boolean bypassWelcome;
public AbstractEndToEndTest(Class<T> activityClass) { public AbstractEndToEndTest(Class<T> activityClass) {
@ -29,9 +34,22 @@ public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityI
this.bypassWelcome = bypassWelcome; this.bypassWelcome = bypassWelcome;
} }
@BeforeClass
public static void beforeClass() {
ApplicationState.getInstance().stubMailServer = new StubMailServer();
}
@AfterClass
public static void afterClass() {
ApplicationState.getInstance().stubMailServer.stop();
}
@Before
@Override @Override
protected void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
getActivity(); getActivity();
if (bypassWelcome) { if (bypassWelcome) {
@ -39,6 +57,12 @@ public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityI
} }
} }
@After
@Override
public void tearDown() throws Exception {
super.tearDown();
}
private void bypassWelcomeScreen() { private void bypassWelcomeScreen() {
try { try {
onView(withId(R.id.welcome_message)).check(ViewAssertions.doesNotExist()); onView(withId(R.id.welcome_message)).check(ViewAssertions.doesNotExist());
@ -47,14 +71,12 @@ public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityI
* The view doesn't NOT exist == the view exists, and needs to be bypassed! * The view doesn't NOT exist == the view exists, and needs to be bypassed!
*/ */
Log.d(getClass().getName(), "Bypassing welcome"); Log.d(getClass().getName(), "Bypassing welcome");
new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage()); new AccountSetupFlow().setupAccountFromWelcomePage(new WelcomeMessagePage());
} }
} }
protected StubMailServer setupMailServer() {
if (null == state.stubMailServer) { public void testEmpty() {
state.stubMailServer = new StubMailServer(); // workaround, needs to be empty so that JUnit4 test gets picked up
}
return state.stubMailServer;
} }
} }

View File

@ -24,12 +24,6 @@ public class AccountSetupFlow {
static final String ACCOUNT_NAME = "sendAndReceiveTestName"; static final String ACCOUNT_NAME = "sendAndReceiveTestName";
private final AbstractEndToEndTest test;
public AccountSetupFlow(AbstractEndToEndTest test) {
this.test = test;
}
public AccountsPage setupAccountFromWelcomePage(WelcomeMessagePage welcomeMessagePage) { public AccountsPage setupAccountFromWelcomePage(WelcomeMessagePage welcomeMessagePage) {
AccountSetupPage accountSetupPage = welcomeMessagePage.clickNext(); AccountSetupPage accountSetupPage = welcomeMessagePage.clickNext();
return setupAccountFromSetupNewAccountActivity(accountSetupPage); return setupAccountFromSetupNewAccountActivity(accountSetupPage);
@ -45,7 +39,8 @@ public class AccountSetupFlow {
IncomingServerSettingsPage incoming = accountTypePage.clickImap(); IncomingServerSettingsPage incoming = accountTypePage.clickImap();
StubMailServer stubMailServer = test.setupMailServer();
StubMailServer stubMailServer = ApplicationState.getInstance().stubMailServer;
OutgoingServerSettingsPage outgoing = setupIncomingServerAndClickNext(incoming, stubMailServer); OutgoingServerSettingsPage outgoing = setupIncomingServerAndClickNext(incoming, stubMailServer);

View File

@ -1,5 +1,9 @@
package com.fsck.k9.endtoend.framework; package com.fsck.k9.endtoend.framework;
import android.util.Log;
import com.fsck.k9.K9;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup; import com.icegreen.greenmail.util.ServerSetup;
@ -18,7 +22,18 @@ public class StubMailServer {
public StubMailServer() { public StubMailServer() {
greenmail = new GreenMail(new ServerSetup[]{IMAP_SERVER_SETUP, SMTP_SERVER_SETUP}); greenmail = new GreenMail(new ServerSetup[]{IMAP_SERVER_SETUP, SMTP_SERVER_SETUP});
greenmail.setUser(UserForImap.TEST_USER.emailAddress, UserForImap.TEST_USER.loginUsername, UserForImap.TEST_USER.password); GreenMailUser user = greenmail
.setUser(UserForImap.TEST_USER.emailAddress, UserForImap.TEST_USER.loginUsername,
UserForImap.TEST_USER.password);
for (String mailbox : new String[] {"Drafts", "Spam"}) {
Log.d(K9.LOG_TAG, "creating mailbox "+mailbox);
try {
greenmail.getManagers().getImapHostManager().createMailbox(user, mailbox);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
greenmail.start(); greenmail.start();
} }
@ -37,5 +52,9 @@ public class StubMailServer {
public int getImapPort() { public int getImapPort() {
return IMAP_SERVER_SETUP.getPort(); return IMAP_SERVER_SETUP.getPort();
} }
public void stop() {
greenmail.stop();
}
} }

View File

@ -1,8 +1,10 @@
package com.fsck.k9.endtoend.pages; package com.fsck.k9.endtoend.pages;
import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.matcher.ViewMatchers; import android.support.test.espresso.matcher.ViewMatchers;
import android.util.Log;
import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.clearText; import static android.support.test.espresso.action.ViewActions.clearText;
@ -41,6 +43,7 @@ public class AccountSetupNamesPage extends AbstractPage {
try { try {
onView(ViewMatchers.withText("OK")).perform(click()); onView(ViewMatchers.withText("OK")).perform(click());
} catch (NoMatchingViewException ex) { } catch (NoMatchingViewException ex) {
Log.w(K9.LOG_TAG, "did not find Changelog OK button - ignored");
// Ignored. Not the best way of doing this, but Espresso rightly makes // Ignored. Not the best way of doing this, but Espresso rightly makes
// conditional flow difficult. // conditional flow difficult.
} }

View File

@ -1,27 +1,37 @@
package com.fsck.k9.helper; package com.fsck.k9.helper;
import junit.framework.TestCase; import android.support.test.runner.AndroidJUnit4;
import java.lang.String; import org.junit.Test;
import org.junit.runner.RunWith;
public class FileHelperTest extends TestCase { import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class FileHelperTest {
@Test
public void testSanitize1() { public void testSanitize1() {
checkSanitization(".._bla_", "../bla_"); checkSanitization(".._bla_", "../bla_");
} }
@Test
public void testSanitize2() { public void testSanitize2() {
checkSanitization("_etc_bla", "/etc/bla"); checkSanitization("_etc_bla", "/etc/bla");
} }
@Test
public void testSanitize3() { public void testSanitize3() {
checkSanitization("_пPп", "+пPп"); checkSanitization("_пPп", "+пPп");
} }
@Test
public void testSanitize4() { public void testSanitize4() {
checkSanitization(".東京_!", ".東京?!"); checkSanitization(".東京_!", ".東京?!");
} }
@Test
public void testSanitize5() { public void testSanitize5() {
checkSanitization("Plan 9", "Plan 9"); checkSanitization("Plan 9", "Plan 9");
} }

View File

@ -1,16 +1,24 @@
package com.fsck.k9.helper; package com.fsck.k9.helper;
import junit.framework.TestCase;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
public class HtmlConverterTest extends TestCase { import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class HtmlConverterTest {
// Useful if you want to write stuff to a file for debugging in a browser. // Useful if you want to write stuff to a file for debugging in a browser.
private static final boolean WRITE_TO_FILE = Boolean.parseBoolean(System.getProperty("k9.htmlConverterTest.writeToFile", "false")); private static final boolean WRITE_TO_FILE = Boolean.parseBoolean(System.getProperty("k9.htmlConverterTest.writeToFile", "false"));
private static final String OUTPUT_FILE = "C:/temp/parse.html"; private static final String OUTPUT_FILE = "C:/temp/parse.html";
@Test
public void testTextQuoteToHtmlBlockquote() { public void testTextQuoteToHtmlBlockquote() {
String message = "Panama!\r\n" + String message = "Panama!\r\n" +
"\r\n" + "\r\n" +
@ -26,28 +34,33 @@ public class HtmlConverterTest extends TestCase {
String result = HtmlConverter.textToHtml(message); String result = HtmlConverter.textToHtml(message);
writeToFile(result); writeToFile(result);
assertEquals("<pre class=\"k9mail\">" assertEquals("<pre class=\"k9mail\">"
+ "Panama!<br />" + "Panama!<br />"
+ "<br />" + "<br />"
+ "Bob Barker &lt;bob@aol.com&gt; wrote:<br />" + "Bob Barker &lt;bob@aol.com&gt; wrote:<br />"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">" +
+ " a canal<br />" "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">"
+ "<br />" + " a canal<br />"
+ " Dorothy Jo Gideon &lt;dorothy@aol.com&gt; espoused:<br />" + "<br />"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">" + " Dorothy Jo Gideon &lt;dorothy@aol.com&gt; espoused:<br />"
+ "A man, a plan...<br />" +
+ "</blockquote>" "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">"
+ " Too easy!<br />" + "A man, a plan...<br />"
+ "</blockquote>" + "</blockquote>"
+ "<br />" + " Too easy!<br />"
+ "Nice job :)<br />" + "</blockquote>"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">" + "<br />"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">" + "Nice job :)<br />"
+ " Guess!" +
+ "</blockquote>" "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">"
+ "</blockquote>" +
+ "</pre>", result); "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">"
+ " Guess!"
+ "</blockquote>"
+ "</blockquote>"
+ "</pre>", result);
} }
@Test
public void testTextQuoteToHtmlBlockquoteIndented() { public void testTextQuoteToHtmlBlockquoteIndented() {
String message = "*facepalm*\r\n" + String message = "*facepalm*\r\n" +
"\r\n" + "\r\n" +
@ -73,6 +86,7 @@ public class HtmlConverterTest extends TestCase {
} }
@Test
public void testQuoteDepthColor() { public void testQuoteDepthColor() {
assertEquals(HtmlConverter.getQuoteColor(1), HtmlConverter.QUOTE_COLOR_LEVEL_1); assertEquals(HtmlConverter.getQuoteColor(1), HtmlConverter.QUOTE_COLOR_LEVEL_1);
assertEquals(HtmlConverter.getQuoteColor(2), HtmlConverter.QUOTE_COLOR_LEVEL_2); assertEquals(HtmlConverter.getQuoteColor(2), HtmlConverter.QUOTE_COLOR_LEVEL_2);
@ -135,6 +149,7 @@ public class HtmlConverterTest extends TestCase {
} }
} }
@Test
public void testPreserveSpacesAtFirst() { public void testPreserveSpacesAtFirst() {
String message = "foo\r\n" String message = "foo\r\n"
+ " bar\r\n" + " bar\r\n"
@ -148,6 +163,7 @@ public class HtmlConverterTest extends TestCase {
+ "</pre>", result); + "</pre>", result);
} }
@Test
public void testPreserveSpacesAtFirstForSpecialCharacters() { public void testPreserveSpacesAtFirstForSpecialCharacters() {
String message = String message =
" \r\n" " \r\n"
@ -168,6 +184,7 @@ public class HtmlConverterTest extends TestCase {
+ "</pre>", result); + "</pre>", result);
} }
@Test
public void testLinkifyBitcoinAndHttpUri() { public void testLinkifyBitcoinAndHttpUri() {
String text = "bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU http://example.com/"; String text = "bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU http://example.com/";

View File

@ -1,21 +1,31 @@
package com.fsck.k9.helper; package com.fsck.k9.helper;
import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.test.AndroidTestCase; import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString; import android.text.SpannableString;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
public class MessageHelperTest extends AndroidTestCase { import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
public class MessageHelperTest {
private Contacts contacts; private Contacts contacts;
private Contacts mockContacts; private Contacts mockContacts;
@Override @Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); Context context = InstrumentationRegistry.getTargetContext();
contacts = new Contacts(getContext()); contacts = new Contacts(context);
mockContacts = new Contacts(getContext()) { mockContacts = new Contacts(context) {
@Override public String getNameForAddress(String address) { @Override public String getNameForAddress(String address) {
if ("test@testor.com".equals(address)) { if ("test@testor.com".equals(address)) {
return "Tim Testor"; return "Tim Testor";
@ -26,16 +36,19 @@ public class MessageHelperTest extends AndroidTestCase {
}; };
} }
@Test
public void testToFriendlyShowsPersonalPartIfItExists() throws Exception { public void testToFriendlyShowsPersonalPartIfItExists() throws Exception {
Address address = new Address("test@testor.com", "Tim Testor"); Address address = new Address("test@testor.com", "Tim Testor");
assertEquals("Tim Testor", MessageHelper.toFriendly(address, contacts)); assertEquals("Tim Testor", MessageHelper.toFriendly(address, contacts));
} }
@Test
public void testToFriendlyShowsEmailPartIfNoPersonalPartExists() throws Exception { public void testToFriendlyShowsEmailPartIfNoPersonalPartExists() throws Exception {
Address address = new Address("test@testor.com"); Address address = new Address("test@testor.com");
assertEquals("test@testor.com", MessageHelper.toFriendly(address, contacts)); assertEquals("test@testor.com", MessageHelper.toFriendly(address, contacts));
} }
@Test
public void testToFriendlyArray() throws Exception { public void testToFriendlyArray() throws Exception {
Address address1 = new Address("test@testor.com", "Tim Testor"); Address address1 = new Address("test@testor.com", "Tim Testor");
Address address2 = new Address("foo@bar.com", "Foo Bar"); Address address2 = new Address("foo@bar.com", "Foo Bar");
@ -43,11 +56,13 @@ public class MessageHelperTest extends AndroidTestCase {
assertEquals("Tim Testor,Foo Bar", MessageHelper.toFriendly(addresses, contacts).toString()); assertEquals("Tim Testor,Foo Bar", MessageHelper.toFriendly(addresses, contacts).toString());
} }
@Test
public void testToFriendlyWithContactLookup() throws Exception { public void testToFriendlyWithContactLookup() throws Exception {
Address address = new Address("test@testor.com"); Address address = new Address("test@testor.com");
assertEquals("Tim Testor", MessageHelper.toFriendly(address, mockContacts).toString()); assertEquals("Tim Testor", MessageHelper.toFriendly(address, mockContacts).toString());
} }
@Test
public void testToFriendlyWithChangeContactColor() throws Exception { public void testToFriendlyWithChangeContactColor() throws Exception {
Address address = new Address("test@testor.com"); Address address = new Address("test@testor.com");
CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, true, true, Color.RED); CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, true, true, Color.RED);
@ -55,6 +70,7 @@ public class MessageHelperTest extends AndroidTestCase {
assertEquals("Tim Testor", friendly.toString()); assertEquals("Tim Testor", friendly.toString());
} }
@Test
public void testToFriendlyWithoutCorrespondentNames() throws Exception { public void testToFriendlyWithoutCorrespondentNames() throws Exception {
Address address = new Address("test@testor.com", "Tim Testor"); Address address = new Address("test@testor.com", "Tim Testor");
CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, false, false, 0); CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, false, false, 0);

View File

@ -8,12 +8,8 @@ import java.io.OutputStream;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
import com.fsck.k9.mail.internet.MimeMessageHelper; import android.support.test.InstrumentationRegistry;
import org.apache.commons.io.IOUtils; import android.support.test.runner.AndroidJUnit4;
import org.apache.james.mime4j.codec.Base64InputStream;
import org.apache.james.mime4j.util.MimeUtil;
import android.test.AndroidTestCase;
import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.fsck.k9.mail.internet.BinaryTempFileBody;
@ -22,13 +18,23 @@ import com.fsck.k9.mail.internet.CharsetSupport;
import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeHeader;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeMessageHelper;
import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.util.MimeUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
public class MessageTest extends AndroidTestCase { import static org.junit.Assert.assertEquals;
@Override import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4.class)
public class MessageTest {
@Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp();
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo")); TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
} }
@ -272,10 +278,7 @@ public class MessageTest extends AndroidTestCase {
private int mMimeBoundary; private int mMimeBoundary;
public MessageTest() { @Test
super();
}
public void testSetSendDateSetsSentDate() throws Exception { public void testSetSendDateSetsSentDate() throws Exception {
Message message = sampleMessage(); Message message = sampleMessage();
final int milliseconds = 0; final int milliseconds = 0;
@ -286,23 +289,26 @@ public class MessageTest extends AndroidTestCase {
assertEquals(milliseconds, sentDate.getTime()); assertEquals(milliseconds, sentDate.getTime());
} }
@Test
public void testSetSendDateFormatsHeaderCorrectlyWithCurrentTimeZone() throws Exception { public void testSetSendDateFormatsHeaderCorrectlyWithCurrentTimeZone() throws Exception {
Message message = sampleMessage(); Message message = sampleMessage();
message.setSentDate(new Date(0), false); message.setSentDate(new Date(0), false);
assertEquals("Thu, 01 Jan 1970 09:00:00 +0900", message.getHeader("Date")[0]); assertEquals("Thu, 01 Jan 1970 09:00:00 +0900", message.getHeader("Date")[0]);
} }
@Test
public void testSetSendDateFormatsHeaderCorrectlyWithoutTimeZone() throws Exception { public void testSetSendDateFormatsHeaderCorrectlyWithoutTimeZone() throws Exception {
Message message = sampleMessage(); Message message = sampleMessage();
message.setSentDate(new Date(0), true); message.setSentDate(new Date(0), true);
assertEquals("Thu, 01 Jan 1970 00:00:00 +0000", message.getHeader("Date")[0]); assertEquals("Thu, 01 Jan 1970 00:00:00 +0000", message.getHeader("Date")[0]);
} }
@Test
public void testMessage() throws MessagingException, IOException { public void testMessage() throws MessagingException, IOException {
MimeMessage message; MimeMessage message;
ByteArrayOutputStream out; ByteArrayOutputStream out;
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir()); BinaryTempFileBody.setTempDirectory(InstrumentationRegistry.getTargetContext().getCacheDir());
mMimeBoundary = 101; mMimeBoundary = 101;
message = nestedMessage(nestedMessage(sampleMessage())); message = nestedMessage(nestedMessage(sampleMessage()));

View File

@ -6,10 +6,13 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import android.test.AndroidTestCase; import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.fsck.k9.mail.internet.BinaryTempFileBody;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.spongycastle.openpgp.PGPCompressedData; import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
@ -22,8 +25,11 @@ import org.spongycastle.openpgp.bc.BcPGPObjectFactory;
import org.spongycastle.openpgp.bc.BcPGPPublicKeyRingCollection; import org.spongycastle.openpgp.bc.BcPGPPublicKeyRingCollection;
import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import static junit.framework.Assert.assertTrue;
public class PgpMimeMessageTest extends AndroidTestCase {
@RunWith(AndroidJUnit4.class)
public class PgpMimeMessageTest {
private static final String PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + private static final String PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Version: GnuPG v1\n" + "Version: GnuPG v1\n" +
"\n" + "\n" +
@ -150,6 +156,7 @@ public class PgpMimeMessageTest extends AndroidTestCase {
"-----END PGP PUBLIC KEY BLOCK-----\n"; "-----END PGP PUBLIC KEY BLOCK-----\n";
@Test
public void testSignedMessage() throws IOException, MessagingException, PGPException { public void testSignedMessage() throws IOException, MessagingException, PGPException {
String messageSource = "Date: Mon, 08 Dec 2014 17:44:18 +0100\r\n" + String messageSource = "Date: Mon, 08 Dec 2014 17:44:18 +0100\r\n" +
"From: cketti <cketti@googlemail.com>\r\n" + "From: cketti <cketti@googlemail.com>\r\n" +
@ -209,7 +216,7 @@ public class PgpMimeMessageTest extends AndroidTestCase {
"\r\n" + "\r\n" +
"--24Bem7EnUI1Ipn9jNXuLgsetqa6wOkIxM--\r\n"; "--24Bem7EnUI1Ipn9jNXuLgsetqa6wOkIxM--\r\n";
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir()); BinaryTempFileBody.setTempDirectory(InstrumentationRegistry.getTargetContext().getCacheDir());
InputStream messageInputStream = new ByteArrayInputStream(messageSource.getBytes()); InputStream messageInputStream = new ByteArrayInputStream(messageSource.getBytes());
MimeMessage message; MimeMessage message;

View File

@ -6,14 +6,22 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.fsck.k9.mail.internet.BinaryTempFileBody;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertEquals;
public class ReconstructMessageTest extends AndroidTestCase { @RunWith(AndroidJUnit4.class)
public class ReconstructMessageTest {
@Test
public void testMessage() throws IOException, MessagingException { public void testMessage() throws IOException, MessagingException {
String messageSource = String messageSource =
"From: from@example.com\r\n" + "From: from@example.com\r\n" +
@ -45,7 +53,7 @@ public class ReconstructMessageTest extends AndroidTestCase {
"------Boundary--\r\n" + "------Boundary--\r\n" +
"Hi, I'm the epilogue"; "Hi, I'm the epilogue";
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir()); BinaryTempFileBody.setTempDirectory(InstrumentationRegistry.getTargetContext().getCacheDir());
InputStream messageInputStream = new ByteArrayInputStream(messageSource.getBytes()); InputStream messageInputStream = new ByteArrayInputStream(messageSource.getBytes());
MimeMessage message; MimeMessage message;

View File

@ -1,18 +1,29 @@
package com.fsck.k9.mail.ssl; package com.fsck.k9.mail.ssl;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import android.test.AndroidTestCase;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import javax.net.ssl.X509TrustManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
/** /**
* Test the functionality of {@link TrustManagerFactory}. * Test the functionality of {@link TrustManagerFactory}.
*/ */
public class TrustManagerFactoryTest extends AndroidTestCase { @RunWith(AndroidJUnit4.class)
public class TrustManagerFactoryTest {
public static final String MATCHING_HOST = "k9.example.com"; public static final String MATCHING_HOST = "k9.example.com";
public static final String NOT_MATCHING_HOST = "bla.example.com"; public static final String NOT_MATCHING_HOST = "bla.example.com";
public static final int PORT1 = 993; public static final int PORT1 = 993;
@ -196,15 +207,16 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
new ByteArrayInputStream(encodedCert.getBytes())); new ByteArrayInputStream(encodedCert.getBytes()));
} }
@Override @Before
public void setUp() throws Exception { public void setUp() throws Exception {
mKeyStoreFile = File.createTempFile("localKeyStore", null, getContext().getCacheDir()); mKeyStoreFile = File.createTempFile("localKeyStore", null,
InstrumentationRegistry.getTargetContext().getCacheDir());
mKeyStore = LocalKeyStore.getInstance(); mKeyStore = LocalKeyStore.getInstance();
mKeyStore.setKeyStoreFile(mKeyStoreFile); mKeyStore.setKeyStoreFile(mKeyStoreFile);
} }
@Override @After
protected void tearDown() { public void tearDown() {
mKeyStoreFile.delete(); mKeyStoreFile.delete();
} }
@ -220,6 +232,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
* @throws Exception * @throws Exception
* if anything goes wrong * if anything goes wrong
*/ */
@Test
public void testDifferentCertificatesOnSameServer() throws Exception { public void testDifferentCertificatesOnSameServer() throws Exception {
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1); mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2); mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2);
@ -230,24 +243,28 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
trustManager1.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType"); trustManager1.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
} }
@Test
public void testSelfSignedCertificateMatchingHost() throws Exception { public void testSelfSignedCertificateMatchingHost() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1); mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1); X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType"); trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
} }
@Test
public void testSelfSignedCertificateNotMatchingHost() throws Exception { public void testSelfSignedCertificateNotMatchingHost() throws Exception {
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1); mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1); X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType"); trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
} }
@Test
public void testWrongCertificate() throws Exception { public void testWrongCertificate() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1); mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1); X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 }); assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
} }
@Test
public void testCertificateOfOtherHost() throws Exception { public void testCertificateOfOtherHost() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1); mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
mKeyStore.addCertificate(MATCHING_HOST, PORT2, mCert2); mKeyStore.addCertificate(MATCHING_HOST, PORT2, mCert2);
@ -256,11 +273,13 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 }); assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
} }
@Test
public void testUntrustedCertificateChain() throws Exception { public void testUntrustedCertificateChain() throws Exception {
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1); X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
assertCertificateRejection(trustManager, new X509Certificate[] { mCert3, mCaCert }); assertCertificateRejection(trustManager, new X509Certificate[] { mCert3, mCaCert });
} }
@Test
public void testLocallyTrustedCertificateChain() throws Exception { public void testLocallyTrustedCertificateChain() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert3); mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert3);
@ -268,6 +287,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType"); trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
} }
@Test
public void testLocallyTrustedCertificateChainNotMatchingHost() throws Exception { public void testLocallyTrustedCertificateChainNotMatchingHost() throws Exception {
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert3); mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert3);
@ -275,17 +295,20 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType"); trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
} }
@Test
public void testGloballyTrustedCertificateChain() throws Exception { public void testGloballyTrustedCertificateChain() throws Exception {
X509TrustManager trustManager = TrustManagerFactory.get("www.linux.com", PORT1); X509TrustManager trustManager = TrustManagerFactory.get("www.linux.com", PORT1);
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert}; X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert};
trustManager.checkServerTrusted(certificates, "authType"); trustManager.checkServerTrusted(certificates, "authType");
} }
@Test
public void testGloballyTrustedCertificateNotMatchingHost() throws Exception { public void testGloballyTrustedCertificateNotMatchingHost() throws Exception {
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1); X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
assertCertificateRejection(trustManager, new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert}); assertCertificateRejection(trustManager, new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert});
} }
@Test
public void testGloballyTrustedCertificateNotMatchingHostOverride() throws Exception { public void testGloballyTrustedCertificateNotMatchingHostOverride() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mLinuxComCert); mKeyStore.addCertificate(MATCHING_HOST, PORT1, mLinuxComCert);
@ -306,6 +329,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
assertFalse("The certificate should have been rejected but wasn't", certificateValid); assertFalse("The certificate should have been rejected but wasn't", certificateValid);
} }
@Test
public void testKeyStoreLoading() throws Exception { public void testKeyStoreLoading() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1); mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2); mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2);

View File

@ -0,0 +1,204 @@
package com.fsck.k9.mail.store.imap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.endtoend.framework.StubMailServer;
import com.fsck.k9.endtoend.framework.UserForImap;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.MessagingException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertArrayEquals;
@RunWith(AndroidJUnit4.class)
public class ImapConnectionTest {
private static final String[] CAPABILITIES = new String[] { "IMAP4REV1", "LITERAL+", "QUOTA" };
private StubMailServer stubMailServer;
private ImapConnection connection;
private TestImapSettings settings;
@Before
public void setUp() throws Exception {
stubMailServer = new StubMailServer();
settings = new TestImapSettings(UserForImap.TEST_USER);
connection = new ImapConnection(settings, null, null);
}
@After
public void tearDown() throws Exception {
stubMailServer.stop();
}
@Test(expected = MessagingException.class)
public void testOpenConnectionWithoutRunningServerThrowsMessagingException() throws Exception {
stubMailServer.stop();
connection.open();
}
@Test(expected = AuthenticationFailedException.class)
public void testOpenConnectionWithWrongCredentialsThrowsAuthenticationFailedException() throws Exception {
connection = new ImapConnection(new TestImapSettings("wrong", "password"), null, null);
connection.open();
}
@Test
public void testConnectionIsInitiallyClosed() throws Exception {
assertFalse(connection.isOpen());
}
@Test
public void testSuccessfulOpenConnectionTogglesOpenState() throws Exception {
connection.open();
assertTrue(connection.isOpen());
}
@Test
public void testSuccessfulOpenAndCloseConnectionTogglesOpenState() throws Exception {
connection.open();
connection.close();
assertFalse(connection.isOpen());
}
@Test
public void testCapabilitiesAreInitiallyEmpty() throws Exception {
assertTrue(connection.getCapabilities().isEmpty());
}
@Test
public void testCapabilitiesListGetsParsedCorrectly() throws Exception {
connection.open();
List<String> capabilities = new ArrayList<String>(connection.getCapabilities());
Collections.sort(capabilities);
assertArrayEquals(CAPABILITIES, capabilities.toArray());
}
@Test
public void testHasCapabilityChecks() throws Exception {
connection.open();
for (String capability : CAPABILITIES) {
assertTrue(connection.hasCapability(capability));
}
assertFalse(connection.hasCapability("FROBAZIFCATE"));
}
@Test
public void testPathPrefixGetsSetCorrectly() throws Exception {
connection.open();
assertEquals("", settings.getPathPrefix());
}
@Test
public void testPathDelimiterGetsParsedCorrectly() throws Exception {
connection.open();
assertEquals(".", settings.getPathDelimiter());
}
@Test
public void testCombinedPrefixGetsSetCorrectly() throws Exception {
connection.open();
assertNull(settings.getCombinedPrefix());
}
private class TestImapSettings implements ImapSettings {
private String pathPrefix;
private String pathDelimiter;
private String username;
private String password;
private String combinedPrefix;
public TestImapSettings(UserForImap userForImap) {
this(userForImap.loginUsername, userForImap.password);
}
public TestImapSettings(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String getHost() {
return stubMailServer.getImapBindAddress();
}
@Override
public int getPort() {
return stubMailServer.getImapPort();
}
@Override
public ConnectionSecurity getConnectionSecurity() {
return ConnectionSecurity.NONE;
}
@Override
public AuthType getAuthType() {
return AuthType.PLAIN;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getClientCertificateAlias() {
return null;
}
@Override
public boolean useCompression(int type) {
return false;
}
@Override
public String getPathPrefix() {
return pathPrefix;
}
@Override
public void setPathPrefix(String prefix) {
pathPrefix = prefix;
}
@Override
public String getPathDelimiter() {
return pathDelimiter;
}
@Override
public void setPathDelimiter(String delimiter) {
pathDelimiter = delimiter;
}
@Override
public String getCombinedPrefix() {
return combinedPrefix;
}
@Override
public void setCombinedPrefix(String prefix) {
combinedPrefix = prefix;
}
}
}

View File

@ -3,22 +3,30 @@ package com.fsck.k9.mailstore;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import android.test.AndroidTestCase;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.activity.K9ActivityCommon; import com.fsck.k9.activity.K9ActivityCommon;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeMessageHelper; import com.fsck.k9.mail.internet.MimeMessageHelper;
import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.fsck.k9.mailstore.LocalMessageExtractor.extractTextAndAttachments; import static com.fsck.k9.mailstore.LocalMessageExtractor.extractTextAndAttachments;
import static junit.framework.Assert.assertEquals;
public class LocalMessageExtractorTest extends AndroidTestCase {
@RunWith(AndroidJUnit4.class)
public class LocalMessageExtractorTest {
@Test
public void testSimplePlainTextMessage() throws MessagingException { public void testSimplePlainTextMessage() throws MessagingException {
String bodyText = "K-9 Mail rocks :>"; String bodyText = "K-9 Mail rocks :>";
@ -30,7 +38,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, body); MimeMessageHelper.setBody(message, body);
// Extract text // Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message); ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText = bodyText; String expectedText = bodyText;
String expectedHtml = String expectedHtml =
@ -42,6 +50,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
assertEquals(expectedHtml, container.html); assertEquals(expectedHtml, container.html);
} }
@Test
public void testSimpleHtmlMessage() throws MessagingException { public void testSimpleHtmlMessage() throws MessagingException {
String bodyText = "<strong>K-9 Mail</strong> rocks :&gt;"; String bodyText = "<strong>K-9 Mail</strong> rocks :&gt;";
@ -54,7 +63,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, body); MimeMessageHelper.setBody(message, body);
// Extract text // Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message); ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText = "K-9 Mail rocks :>"; String expectedText = "K-9 Mail rocks :>";
String expectedHtml = String expectedHtml =
@ -64,6 +73,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
assertEquals(expectedHtml, container.html); assertEquals(expectedHtml, container.html);
} }
@Test
public void testMultipartPlainTextMessage() throws MessagingException { public void testMultipartPlainTextMessage() throws MessagingException {
String bodyText1 = "text body 1"; String bodyText1 = "text body 1";
String bodyText2 = "text body 2"; String bodyText2 = "text body 2";
@ -84,7 +94,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, multipart); MimeMessageHelper.setBody(message, multipart);
// Extract text // Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message); ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText = String expectedText =
bodyText1 + "\r\n\r\n" + bodyText1 + "\r\n\r\n" +
@ -105,8 +115,9 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
assertEquals(expectedHtml, container.html); assertEquals(expectedHtml, container.html);
} }
@Test
public void testTextPlusRfc822Message() throws MessagingException { public void testTextPlusRfc822Message() throws MessagingException {
K9ActivityCommon.setLanguage(getContext(), "en"); K9ActivityCommon.setLanguage(InstrumentationRegistry.getTargetContext(), "en");
Locale.setDefault(Locale.US); Locale.setDefault(Locale.US);
TimeZone.setDefault(TimeZone.getTimeZone("GMT+01:00")); TimeZone.setDefault(TimeZone.getTimeZone("GMT+01:00"));
@ -140,7 +151,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, multipart); MimeMessageHelper.setBody(message, multipart);
// Extract text // Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message); ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText = String expectedText =
bodyText + bodyText +

View File

@ -1894,9 +1894,15 @@ public class Account implements BaseAccount, StoreConfig {
public void deleteCertificates() { public void deleteCertificates() {
LocalKeyStore localKeyStore = LocalKeyStore.getInstance(); LocalKeyStore localKeyStore = LocalKeyStore.getInstance();
Uri uri = Uri.parse(getStoreUri()); String storeUri = getStoreUri();
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort()); if (storeUri != null) {
uri = Uri.parse(getTransportUri()); Uri uri = Uri.parse(storeUri);
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort()); localKeyStore.deleteCertificate(uri.getHost(), uri.getPort());
}
String transportUri = getTransportUri();
if (transportUri != null) {
Uri uri = Uri.parse(transportUri);
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort());
}
} }
} }

View File

@ -8,13 +8,12 @@ import android.app.FragmentTransaction;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Process;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
@ -87,7 +86,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
setContentView(R.layout.account_setup_check_settings); setContentView(R.layout.account_setup_check_settings);
mMessageView = (TextView)findViewById(R.id.message); mMessageView = (TextView)findViewById(R.id.message);
mProgressBar = (ProgressBar)findViewById(R.id.progress); mProgressBar = (ProgressBar)findViewById(R.id.progress);
((Button)findViewById(R.id.cancel)).setOnClickListener(this); findViewById(R.id.cancel).setOnClickListener(this);
setMessage(R.string.account_setup_check_settings_retr_info_msg); setMessage(R.string.account_setup_check_settings_retr_info_msg);
mProgressBar.setIndeterminate(true); mProgressBar.setIndeterminate(true);
@ -96,82 +95,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
mAccount = Preferences.getPreferences(this).getAccount(accountUuid); mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
mDirection = (CheckDirection) getIntent().getSerializableExtra(EXTRA_CHECK_DIRECTION); mDirection = (CheckDirection) getIntent().getSerializableExtra(EXTRA_CHECK_DIRECTION);
new Thread() { new CheckAccountTask(mAccount).execute(mDirection);
@Override
public void run() {
Store store = null;
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
if (mDestroyed) {
return;
}
if (mCanceled) {
finish();
return;
}
final MessagingController ctrl = MessagingController.getInstance(getApplication());
ctrl.clearCertificateErrorNotifications(AccountSetupCheckSettings.this,
mAccount, mDirection);
if (mDirection == CheckDirection.INCOMING) {
store = mAccount.getRemoteStore();
if (store instanceof WebDavStore) {
setMessage(R.string.account_setup_check_settings_authenticate);
} else {
setMessage(R.string.account_setup_check_settings_check_incoming_msg);
}
store.checkSettings();
if (store instanceof WebDavStore) {
setMessage(R.string.account_setup_check_settings_fetch);
}
MessagingController.getInstance(getApplication()).listFoldersSynchronous(mAccount, true, null);
MessagingController.getInstance(getApplication()).synchronizeMailbox(mAccount, mAccount.getInboxFolderName(), null, null);
}
if (mDestroyed) {
return;
}
if (mCanceled) {
finish();
return;
}
if (mDirection == CheckDirection.OUTGOING) {
if (!(mAccount.getRemoteStore() instanceof WebDavStore)) {
setMessage(R.string.account_setup_check_settings_check_outgoing_msg);
}
Transport transport = Transport.getInstance(K9.app, mAccount);
transport.close();
transport.open();
transport.close();
}
if (mDestroyed) {
return;
}
if (mCanceled) {
finish();
return;
}
setResult(RESULT_OK);
finish();
} catch (final AuthenticationFailedException afe) {
Log.e(K9.LOG_TAG, "Error while testing settings", afe);
showErrorDialog(
R.string.account_setup_failed_dlg_auth_message_fmt,
afe.getMessage() == null ? "" : afe.getMessage());
} catch (final CertificateValidationException cve) {
handleCertificateValidationException(cve);
} catch (final Throwable t) {
Log.e(K9.LOG_TAG, "Error while testing settings", t);
showErrorDialog(
R.string.account_setup_failed_dlg_server_message_fmt,
(t.getMessage() == null ? "" : t.getMessage()));
}
}
}
.start();
} }
private void handleCertificateValidationException(CertificateValidationException cve) { private void handleCertificateValidationException(CertificateValidationException cve) {
@ -199,14 +123,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
} }
private void setMessage(final int resId) { private void setMessage(final int resId) {
mHandler.post(new Runnable() { mMessageView.setText(getString(resId));
public void run() {
if (mDestroyed) {
return;
}
mMessageView.setText(getString(resId));
}
});
} }
private void acceptKeyDialog(final int msgResId, final CertificateValidationException ex) { private void acceptKeyDialog(final int msgResId, final CertificateValidationException ex) {
@ -266,7 +183,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
for (List<?> subjectAlternativeName : subjectAlternativeNames) { for (List<?> subjectAlternativeName : subjectAlternativeNames) {
Integer type = (Integer)subjectAlternativeName.get(0); Integer type = (Integer)subjectAlternativeName.get(0);
Object value = subjectAlternativeName.get(1); Object value = subjectAlternativeName.get(1);
String name = ""; String name;
switch (type.intValue()) { switch (type.intValue()) {
case 0: case 0:
Log.w(K9.LOG_TAG, "SubjectAltName of type OtherName not supported."); Log.w(K9.LOG_TAG, "SubjectAltName of type OtherName not supported.");
@ -473,4 +390,89 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
default: return ""; default: return "";
} }
} }
class CheckAccountTask extends AsyncTask<CheckDirection, Integer, Void> {
private final Account account;
CheckAccountTask(Account account) {
this.account = account;
}
@Override
protected Void doInBackground(CheckDirection... params) {
final CheckDirection direction = params[0];
try {
if (mDestroyed) {
return null;
}
if (mCanceled) {
finish();
return null;
}
final MessagingController ctrl = MessagingController.getInstance(getApplication());
ctrl.clearCertificateErrorNotifications(AccountSetupCheckSettings.this,
account, direction);
if (direction == CheckDirection.INCOMING) {
Store store = account.getRemoteStore();
if (store instanceof WebDavStore) {
publishProgress(R.string.account_setup_check_settings_authenticate);
} else {
publishProgress(R.string.account_setup_check_settings_check_incoming_msg);
}
store.checkSettings();
if (store instanceof WebDavStore) {
publishProgress(R.string.account_setup_check_settings_fetch);
}
MessagingController.getInstance(getApplication()).listFoldersSynchronous(account, true, null);
MessagingController.getInstance(getApplication())
.synchronizeMailbox(account, account.getInboxFolderName(), null, null);
}
if (mDestroyed) {
return null;
}
if (mCanceled) {
finish();
return null;
}
if (direction == CheckDirection.OUTGOING) {
if (!(account.getRemoteStore() instanceof WebDavStore)) {
publishProgress(R.string.account_setup_check_settings_check_outgoing_msg);
}
Transport transport = Transport.getInstance(K9.app, account);
transport.close();
transport.open();
transport.close();
}
if (mDestroyed) {
return null;
}
if (mCanceled) {
finish();
return null;
}
setResult(RESULT_OK);
finish();
} catch (AuthenticationFailedException afe) {
Log.e(K9.LOG_TAG, "Error while testing settings", afe);
showErrorDialog(
R.string.account_setup_failed_dlg_auth_message_fmt,
afe.getMessage() == null ? "" : afe.getMessage());
} catch (CertificateValidationException cve) {
handleCertificateValidationException(cve);
} catch (Throwable t) {
Log.e(K9.LOG_TAG, "Error while testing settings", t);
showErrorDialog(
R.string.account_setup_failed_dlg_server_message_fmt,
(t.getMessage() == null ? "" : t.getMessage()));
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
setMessage(values[0]);
}
}
} }

View File

@ -0,0 +1,54 @@
package com.fsck.k9.helper;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.HtmlSerializer;
import org.htmlcleaner.SimpleHtmlSerializer;
import org.htmlcleaner.TagNode;
public class HtmlSanitizer {
private static final HtmlCleaner HTML_CLEANER;
private static final HtmlSerializer HTML_SERIALIZER;
static {
CleanerProperties properties = createCleanerProperties();
HTML_CLEANER = new HtmlCleaner(properties);
HTML_SERIALIZER = new SimpleHtmlSerializer(properties);
}
private HtmlSanitizer() {}
public static String sanitize(String html) {
TagNode rootNode = HTML_CLEANER.clean(html);
removeMetaRefresh(rootNode);
return HTML_SERIALIZER.getAsString(rootNode, "UTF8");
}
private static CleanerProperties createCleanerProperties() {
CleanerProperties properties = new CleanerProperties();
// See http://htmlcleaner.sourceforge.net/parameters.php for descriptions
properties.setNamespacesAware(false);
properties.setAdvancedXmlEscape(false);
properties.setOmitXmlDeclaration(true);
properties.setOmitDoctypeDeclaration(false);
properties.setTranslateSpecialEntities(false);
properties.setRecognizeUnicodeChars(false);
return properties;
}
private static void removeMetaRefresh(TagNode rootNode) {
for (TagNode element : rootNode.getElementListByName("meta", true)) {
String httpEquiv = element.getAttributeByName("http-equiv");
if (httpEquiv != null && httpEquiv.trim().equalsIgnoreCase("refresh")) {
element.removeFromTree();
}
}
}
}

View File

@ -11,6 +11,8 @@ import android.widget.Toast;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.HtmlSanitizer;
public class MessageWebView extends RigidWebView { public class MessageWebView extends RigidWebView {
@ -123,7 +125,9 @@ public class MessageWebView extends RigidWebView {
} }
content += HtmlConverter.cssStylePre(); content += HtmlConverter.cssStylePre();
content += "</head><body>" + text + "</body></html>"; content += "</head><body>" + text + "</body></html>";
loadDataWithBaseURL("http://", content, "text/html", "utf-8", null);
String sanitizedContent = HtmlSanitizer.sanitize(content);
loadDataWithBaseURL("http://", sanitizedContent, "text/html", "utf-8", null);
resumeTimers(); resumeTimers();
} }

View File

@ -129,6 +129,10 @@
<incoming uri="imap+ssl+://imap.gmx.com" username="$email" /> <incoming uri="imap+ssl+://imap.gmx.com" username="$email" />
<outgoing uri="smtp+ssl+://mail.gmx.com" username="$email" /> <outgoing uri="smtp+ssl+://mail.gmx.com" username="$email" />
</provider> </provider>
<provider id="zoho.com" label="Zoho Mail" domain="zoho.com">
<incoming uri="imap+ssl+://imap.zoho.com" username="$email" />
<outgoing uri="smtp+tls+://smtp.zoho.com" username="$email" />
</provider>
<!-- Yahoo! Mail Variants --> <!-- Yahoo! Mail Variants -->
<provider id="yahoo" label="Yahoo" domain="yahoo.com"> <provider id="yahoo" label="Yahoo" domain="yahoo.com">

View File

@ -0,0 +1,94 @@
package com.fsck.k9.helper;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class HtmlSanitizerTest {
@Test
public void shouldRemoveMetaRefreshInHead() {
String html = "<html>" +
"<head><meta http-equiv=\"refresh\" content=\"1; URL=http://example.com/\"></head>" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshBetweenHeadAndBody() {
String html = "<html>" +
"<head></head><meta http-equiv=\"refresh\" content=\"1; URL=http://example.com/\">" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshInBody() {
String html = "<html>" +
"<head></head>" +
"<body><meta http-equiv=\"refresh\" content=\"1; URL=http://example.com/\">Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshWithUpperCaseAttributeValue() {
String html = "<html>" +
"<head><meta http-equiv=\"REFRESH\" content=\"1; URL=http://example.com/\"></head>" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshWithMixedCaseAttributeValue() {
String html = "<html>" +
"<head><meta http-equiv=\"Refresh\" content=\"1; URL=http://example.com/\"></head>" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshWithoutQuotesAroundAttributeValue() {
String html = "<html>" +
"<head><meta http-equiv=refresh content=\"1; URL=http://example.com/\"></head>" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshWithSpacesInAttributeValue() {
String html = "<html>" +
"<head><meta http-equiv=\"refresh \" content=\"1; URL=http://example.com/\"></head>" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMultipleMetaRefreshTags() {
String html = "<html>" +
"<head><meta http-equiv=\"refresh\" content=\"1; URL=http://example.com/\"></head>" +
"<body><meta http-equiv=\"refresh\" content=\"1; URL=http://example.com/\">Message</body>" +
"</html>";
assertEquals("<html><head></head><body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
@Test
public void shouldRemoveMetaRefreshButKeepOtherMetaTags() {
String html = "<html>" +
"<head>" +
"<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">" +
"<meta http-equiv=\"refresh\" content=\"1; URL=http://example.com/\">" +
"</head>" +
"<body>Message</body>" +
"</html>";
assertEquals("<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" /></head>" +
"<body>Message</body></html>", HtmlSanitizer.sanitize(html));
}
}