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 {
minSdkVersion 15
targetSdkVersion 17
targetSdkVersion 21
}
lintOptions {
abortOnError false
abortOnError true
warningsAsErrors true
lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
}

View File

@ -555,14 +555,14 @@ class ImapConnection {
private static Socket connect(ImapSettings settings, TrustedSocketFactory socketFactory)
throws GeneralSecurityException, MessagingException, IOException {
// Try all IPv4 and IPv6 addresses of the host
InetAddress[] addresses = InetAddress.getAllByName(settings.getHost());
for (int i = 0; i < addresses.length; i++) {
Exception connectException = null;
for (InetAddress address : InetAddress.getAllByName(settings.getHost())) {
try {
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;
if (settings.getConnectionSecurity() == ConnectionSecurity.SSL_TLS_REQUIRED) {
socket = socketFactory.createSocket(
@ -576,15 +576,12 @@ class ImapConnection {
socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
// Successfully connected to the server; don't try any other addresses
return socket;
} catch (SocketException e) {
if (i < (addresses.length - 1)) {
// There are still other addresses for that host to try
continue;
}
throw new MessagingException("Cannot connect to host", e);
} catch (IOException e) {
Log.w(LOG_TAG, "could not connect to "+address, e);
connectException = e;
}
}
throw new MessagingException("Cannot connect to host");
throw new MessagingException("Cannot connect to host", connectException);
}
private void adjustDNSCacheTTL() {
@ -601,10 +598,18 @@ class ImapConnection {
}
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()) {
Log.d(LOG_TAG, "Saving " + capabilities + " capabilities for " + getLogId());
Log.d(LOG_TAG, "Saving " + receivedCapabilities + " capabilities for " + getLogId());
}
capabilities.addAll(receivedCapabilities);
return responses;
}
}

View File

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

View File

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

View File

@ -5,6 +5,9 @@ apply from: '../gradle/plugins/findbugs-android.gradle'
repositories {
jcenter()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
@ -21,16 +24,10 @@ dependencies {
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
androidTestCompile("com.icegreen:greenmail:1.3.1b") {
// Use a better, later version
exclude group: "javax.mail"
androidTestCompile('com.icegreen:greenmail:1.4.1-SNAPSHOT') {
exclude group: 'junit'
}
// 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"
androidTestCompile 'com.madgag.spongycastle:pg:1.51.0.0'
}
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;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.Flag;
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.)
*/
@Test
public void testIdentityStringNoFlag()
{
MessageReference mr = new MessageReference();
@ -22,6 +31,7 @@ public class MessageReferenceTest extends TestCase
/**
* Typically happens during replies.
*/
@Test
public void testIdentityString()
{
MessageReference mr = new MessageReference();
@ -33,6 +43,7 @@ public class MessageReferenceTest extends TestCase
assertEquals("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED", mr.toIdentityString());
}
@Test
public void testParseIdentityStringNoFlag() throws MessagingException
{
MessageReference mr = new MessageReference("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=");
@ -42,6 +53,7 @@ public class MessageReferenceTest extends TestCase
assertNull(mr.flag);
}
@Test
public void testParseIdentityString() throws MessagingException
{
MessageReference mr = new MessageReference("!:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED");
@ -51,24 +63,20 @@ public class MessageReferenceTest extends TestCase
assertEquals(Flag.ANSWERED, mr.flag);
}
@Test
public void testBadVersion() throws MessagingException
{
MessageReference mr = new MessageReference("@:byBoYWkh:Zm9sZGVy:MTAxMDEwMTA=:ANSWERED");
assertNull(mr.accountUuid);
}
@Test(expected = MessagingException.class)
public void testNull() throws MessagingException
{
try
{
new MessageReference(null);
assertTrue(false);
} catch (MessagingException e)
{
assertTrue(true);
}
new MessageReference(null);
}
@Test(expected = MessagingException.class)
public void testCorruption() throws MessagingException
{
MessageReference mr = new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWERED");
@ -78,13 +86,6 @@ public class MessageReferenceTest extends TestCase
assertNotNull(mr.uid);
// Corruption in the Flag should throw MessagingException.
try
{
new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWE!RED");
assertTrue(false);
} catch (MessagingException e)
{
assertTrue(true);
}
new MessageReference("!:%^&%^*$&$by&(BYWkh:Zm9%^@sZGVy:MT-35#$AxMDEwMTA=:ANSWE!RED");
}
}

View File

@ -2,6 +2,8 @@ package com.fsck.k9.endtoend;
import com.fsck.k9.activity.setup.WelcomeMessage;
import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
import org.junit.Test;
/**
* Creates a new IMAP account via the getting started flow.
@ -12,13 +14,14 @@ public class A000_WelcomeAndSetupAccountIntegrationTest extends AbstractEndToEnd
super(WelcomeMessage.class, false);
}
public void testCreateAccount() throws Exception {
new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage());
@Test
public void createAccount() throws Exception {
new AccountSetupFlow().setupAccountFromWelcomePage(new WelcomeMessagePage());
}
public void testCreateSecondAccount() throws Exception {
new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage());
@Test
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.ApplicationState;
import com.fsck.k9.endtoend.pages.AccountsPage;
import org.junit.Test;
/**
* Creates and removes accounts.
@ -17,14 +19,19 @@ public class A010_AccountIntegrationTest extends AbstractEndToEndTest<Accounts>{
super(Accounts.class);
}
public void testCreateAccountDirectly() throws Exception {
new AccountSetupFlow(this).setupAccountFromAccountsPage(new AccountsPage());
@Test
public void createAccountDirectly() throws Exception {
new AccountSetupFlow().setupAccountFromAccountsPage(new AccountsPage());
}
public void testDeleteAccount() {
@Test
public void deleteAccount() {
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);
accountsPage.assertAccountExists(accountForTest.description);

View File

@ -1,6 +1,9 @@
package com.fsck.k9.endtoend;
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.util.Log;
@ -8,16 +11,18 @@ import com.fsck.k9.R;
import com.fsck.k9.endtoend.framework.ApplicationState;
import com.fsck.k9.endtoend.framework.StubMailServer;
import com.fsck.k9.endtoend.pages.WelcomeMessagePage;
import android.support.test.espresso.assertion.ViewAssertions;
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.matcher.ViewMatchers.withId;
@RunWith(AndroidJUnit4.class)
public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
private ApplicationState state = ApplicationState.getInstance();
private final boolean bypassWelcome;
public AbstractEndToEndTest(Class<T> activityClass) {
@ -29,9 +34,22 @@ public abstract class AbstractEndToEndTest<T extends Activity> extends ActivityI
this.bypassWelcome = bypassWelcome;
}
@BeforeClass
public static void beforeClass() {
ApplicationState.getInstance().stubMailServer = new StubMailServer();
}
@AfterClass
public static void afterClass() {
ApplicationState.getInstance().stubMailServer.stop();
}
@Before
@Override
protected void setUp() throws Exception {
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
getActivity();
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() {
try {
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!
*/
Log.d(getClass().getName(), "Bypassing welcome");
new AccountSetupFlow(this).setupAccountFromWelcomePage(new WelcomeMessagePage());
new AccountSetupFlow().setupAccountFromWelcomePage(new WelcomeMessagePage());
}
}
protected StubMailServer setupMailServer() {
if (null == state.stubMailServer) {
state.stubMailServer = new StubMailServer();
}
return state.stubMailServer;
public void testEmpty() {
// workaround, needs to be empty so that JUnit4 test gets picked up
}
}

View File

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

View File

@ -1,5 +1,9 @@
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.ServerSetup;
@ -18,7 +22,18 @@ public class StubMailServer {
public StubMailServer() {
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();
}
@ -37,5 +52,9 @@ public class StubMailServer {
public int getImapPort() {
return IMAP_SERVER_SETUP.getPort();
}
public void stop() {
greenmail.stop();
}
}

View File

@ -1,8 +1,10 @@
package com.fsck.k9.endtoend.pages;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import android.support.test.espresso.NoMatchingViewException;
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.action.ViewActions.clearText;
@ -41,6 +43,7 @@ public class AccountSetupNamesPage extends AbstractPage {
try {
onView(ViewMatchers.withText("OK")).perform(click());
} 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
// conditional flow difficult.
}

View File

@ -1,27 +1,37 @@
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() {
checkSanitization(".._bla_", "../bla_");
}
@Test
public void testSanitize2() {
checkSanitization("_etc_bla", "/etc/bla");
}
@Test
public void testSanitize3() {
checkSanitization("_пPп", "+пPп");
}
@Test
public void testSanitize4() {
checkSanitization(".東京_!", ".東京?!");
}
@Test
public void testSanitize5() {
checkSanitization("Plan 9", "Plan 9");
}

View File

@ -1,16 +1,24 @@
package com.fsck.k9.helper;
import junit.framework.TestCase;
import java.io.BufferedWriter;
import java.io.File;
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.
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";
@Test
public void testTextQuoteToHtmlBlockquote() {
String message = "Panama!\r\n" +
"\r\n" +
@ -26,28 +34,33 @@ public class HtmlConverterTest extends TestCase {
String result = HtmlConverter.textToHtml(message);
writeToFile(result);
assertEquals("<pre class=\"k9mail\">"
+ "Panama!<br />"
+ "<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 />"
+ "<br />"
+ " Dorothy Jo Gideon &lt;dorothy@aol.com&gt; espoused:<br />"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">"
+ "A man, a plan...<br />"
+ "</blockquote>"
+ " Too easy!<br />"
+ "</blockquote>"
+ "<br />"
+ "Nice job :)<br />"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">"
+ "<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">"
+ " Guess!"
+ "</blockquote>"
+ "</blockquote>"
+ "</pre>", result);
+ "Panama!<br />"
+ "<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 />"
+ "<br />"
+ " Dorothy Jo Gideon &lt;dorothy@aol.com&gt; espoused:<br />"
+
"<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">"
+ "A man, a plan...<br />"
+ "</blockquote>"
+ " Too easy!<br />"
+ "</blockquote>"
+ "<br />"
+ "Nice job :)<br />"
+
"<blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">"
+
"<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() {
String message = "*facepalm*\r\n" +
"\r\n" +
@ -73,6 +86,7 @@ public class HtmlConverterTest extends TestCase {
}
@Test
public void testQuoteDepthColor() {
assertEquals(HtmlConverter.getQuoteColor(1), HtmlConverter.QUOTE_COLOR_LEVEL_1);
assertEquals(HtmlConverter.getQuoteColor(2), HtmlConverter.QUOTE_COLOR_LEVEL_2);
@ -135,6 +149,7 @@ public class HtmlConverterTest extends TestCase {
}
}
@Test
public void testPreserveSpacesAtFirst() {
String message = "foo\r\n"
+ " bar\r\n"
@ -148,6 +163,7 @@ public class HtmlConverterTest extends TestCase {
+ "</pre>", result);
}
@Test
public void testPreserveSpacesAtFirstForSpecialCharacters() {
String message =
" \r\n"
@ -168,6 +184,7 @@ public class HtmlConverterTest extends TestCase {
+ "</pre>", result);
}
@Test
public void testLinkifyBitcoinAndHttpUri() {
String text = "bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU http://example.com/";

View File

@ -1,21 +1,31 @@
package com.fsck.k9.helper;
import android.content.Context;
import android.graphics.Color;
import android.test.AndroidTestCase;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString;
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 mockContacts;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
contacts = new Contacts(getContext());
mockContacts = new Contacts(getContext()) {
Context context = InstrumentationRegistry.getTargetContext();
contacts = new Contacts(context);
mockContacts = new Contacts(context) {
@Override public String getNameForAddress(String address) {
if ("test@testor.com".equals(address)) {
return "Tim Testor";
@ -26,16 +36,19 @@ public class MessageHelperTest extends AndroidTestCase {
};
}
@Test
public void testToFriendlyShowsPersonalPartIfItExists() throws Exception {
Address address = new Address("test@testor.com", "Tim Testor");
assertEquals("Tim Testor", MessageHelper.toFriendly(address, contacts));
}
@Test
public void testToFriendlyShowsEmailPartIfNoPersonalPartExists() throws Exception {
Address address = new Address("test@testor.com");
assertEquals("test@testor.com", MessageHelper.toFriendly(address, contacts));
}
@Test
public void testToFriendlyArray() throws Exception {
Address address1 = new Address("test@testor.com", "Tim Testor");
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());
}
@Test
public void testToFriendlyWithContactLookup() throws Exception {
Address address = new Address("test@testor.com");
assertEquals("Tim Testor", MessageHelper.toFriendly(address, mockContacts).toString());
}
@Test
public void testToFriendlyWithChangeContactColor() throws Exception {
Address address = new Address("test@testor.com");
CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, true, true, Color.RED);
@ -55,6 +70,7 @@ public class MessageHelperTest extends AndroidTestCase {
assertEquals("Tim Testor", friendly.toString());
}
@Test
public void testToFriendlyWithoutCorrespondentNames() throws Exception {
Address address = new Address("test@testor.com", "Tim Testor");
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.TimeZone;
import com.fsck.k9.mail.internet.MimeMessageHelper;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.codec.Base64InputStream;
import org.apache.james.mime4j.util.MimeUtil;
import android.test.AndroidTestCase;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.Message.RecipientType;
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.MimeHeader;
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.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 {
@Override
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4.class)
public class MessageTest {
@Before
public void setUp() throws Exception {
super.setUp();
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
}
@ -272,10 +278,7 @@ public class MessageTest extends AndroidTestCase {
private int mMimeBoundary;
public MessageTest() {
super();
}
@Test
public void testSetSendDateSetsSentDate() throws Exception {
Message message = sampleMessage();
final int milliseconds = 0;
@ -286,23 +289,26 @@ public class MessageTest extends AndroidTestCase {
assertEquals(milliseconds, sentDate.getTime());
}
@Test
public void testSetSendDateFormatsHeaderCorrectlyWithCurrentTimeZone() throws Exception {
Message message = sampleMessage();
message.setSentDate(new Date(0), false);
assertEquals("Thu, 01 Jan 1970 09:00:00 +0900", message.getHeader("Date")[0]);
}
@Test
public void testSetSendDateFormatsHeaderCorrectlyWithoutTimeZone() throws Exception {
Message message = sampleMessage();
message.setSentDate(new Date(0), true);
assertEquals("Thu, 01 Jan 1970 00:00:00 +0000", message.getHeader("Date")[0]);
}
@Test
public void testMessage() throws MessagingException, IOException {
MimeMessage message;
ByteArrayOutputStream out;
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir());
BinaryTempFileBody.setTempDirectory(InstrumentationRegistry.getTargetContext().getCacheDir());
mMimeBoundary = 101;
message = nestedMessage(nestedMessage(sampleMessage()));

View File

@ -6,10 +6,13 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
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.MimeMessage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPException;
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.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" +
"Version: GnuPG v1\n" +
"\n" +
@ -150,6 +156,7 @@ public class PgpMimeMessageTest extends AndroidTestCase {
"-----END PGP PUBLIC KEY BLOCK-----\n";
@Test
public void testSignedMessage() throws IOException, MessagingException, PGPException {
String messageSource = "Date: Mon, 08 Dec 2014 17:44:18 +0100\r\n" +
"From: cketti <cketti@googlemail.com>\r\n" +
@ -209,7 +216,7 @@ public class PgpMimeMessageTest extends AndroidTestCase {
"\r\n" +
"--24Bem7EnUI1Ipn9jNXuLgsetqa6wOkIxM--\r\n";
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir());
BinaryTempFileBody.setTempDirectory(InstrumentationRegistry.getTargetContext().getCacheDir());
InputStream messageInputStream = new ByteArrayInputStream(messageSource.getBytes());
MimeMessage message;

View File

@ -6,14 +6,22 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.AndroidTestCase;
import com.fsck.k9.mail.internet.BinaryTempFileBody;
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 {
String messageSource =
"From: from@example.com\r\n" +
@ -45,7 +53,7 @@ public class ReconstructMessageTest extends AndroidTestCase {
"------Boundary--\r\n" +
"Hi, I'm the epilogue";
BinaryTempFileBody.setTempDirectory(getContext().getCacheDir());
BinaryTempFileBody.setTempDirectory(InstrumentationRegistry.getTargetContext().getCacheDir());
InputStream messageInputStream = new ByteArrayInputStream(messageSource.getBytes());
MimeMessage message;

View File

@ -1,18 +1,29 @@
package com.fsck.k9.mail.ssl;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
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}.
*/
public class TrustManagerFactoryTest extends AndroidTestCase {
@RunWith(AndroidJUnit4.class)
public class TrustManagerFactoryTest {
public static final String MATCHING_HOST = "k9.example.com";
public static final String NOT_MATCHING_HOST = "bla.example.com";
public static final int PORT1 = 993;
@ -196,15 +207,16 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
new ByteArrayInputStream(encodedCert.getBytes()));
}
@Override
@Before
public void setUp() throws Exception {
mKeyStoreFile = File.createTempFile("localKeyStore", null, getContext().getCacheDir());
mKeyStoreFile = File.createTempFile("localKeyStore", null,
InstrumentationRegistry.getTargetContext().getCacheDir());
mKeyStore = LocalKeyStore.getInstance();
mKeyStore.setKeyStoreFile(mKeyStoreFile);
}
@Override
protected void tearDown() {
@After
public void tearDown() {
mKeyStoreFile.delete();
}
@ -220,6 +232,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
* @throws Exception
* if anything goes wrong
*/
@Test
public void testDifferentCertificatesOnSameServer() throws Exception {
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2);
@ -230,24 +243,28 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
trustManager1.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
}
@Test
public void testSelfSignedCertificateMatchingHost() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
}
@Test
public void testSelfSignedCertificateNotMatchingHost() throws Exception {
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
}
@Test
public void testWrongCertificate() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
}
@Test
public void testCertificateOfOtherHost() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
mKeyStore.addCertificate(MATCHING_HOST, PORT2, mCert2);
@ -256,11 +273,13 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
}
@Test
public void testUntrustedCertificateChain() throws Exception {
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
assertCertificateRejection(trustManager, new X509Certificate[] { mCert3, mCaCert });
}
@Test
public void testLocallyTrustedCertificateChain() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert3);
@ -268,6 +287,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
}
@Test
public void testLocallyTrustedCertificateChainNotMatchingHost() throws Exception {
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert3);
@ -275,17 +295,20 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
}
@Test
public void testGloballyTrustedCertificateChain() throws Exception {
X509TrustManager trustManager = TrustManagerFactory.get("www.linux.com", PORT1);
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert};
trustManager.checkServerTrusted(certificates, "authType");
}
@Test
public void testGloballyTrustedCertificateNotMatchingHost() throws Exception {
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
assertCertificateRejection(trustManager, new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert});
}
@Test
public void testGloballyTrustedCertificateNotMatchingHostOverride() throws Exception {
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);
}
@Test
public void testKeyStoreLoading() throws Exception {
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
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.Locale;
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.mail.Address;
import com.fsck.k9.mail.MessagingException;
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.MimeMessage;
import com.fsck.k9.mail.internet.MimeMessageHelper;
import com.fsck.k9.mail.internet.MimeMultipart;
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 junit.framework.Assert.assertEquals;
public class LocalMessageExtractorTest extends AndroidTestCase {
@RunWith(AndroidJUnit4.class)
public class LocalMessageExtractorTest {
@Test
public void testSimplePlainTextMessage() throws MessagingException {
String bodyText = "K-9 Mail rocks :>";
@ -30,7 +38,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, body);
// Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message);
ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText = bodyText;
String expectedHtml =
@ -42,6 +50,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
assertEquals(expectedHtml, container.html);
}
@Test
public void testSimpleHtmlMessage() throws MessagingException {
String bodyText = "<strong>K-9 Mail</strong> rocks :&gt;";
@ -54,7 +63,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, body);
// Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message);
ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText = "K-9 Mail rocks :>";
String expectedHtml =
@ -64,6 +73,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
assertEquals(expectedHtml, container.html);
}
@Test
public void testMultipartPlainTextMessage() throws MessagingException {
String bodyText1 = "text body 1";
String bodyText2 = "text body 2";
@ -84,7 +94,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, multipart);
// Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message);
ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText =
bodyText1 + "\r\n\r\n" +
@ -105,8 +115,9 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
assertEquals(expectedHtml, container.html);
}
@Test
public void testTextPlusRfc822Message() throws MessagingException {
K9ActivityCommon.setLanguage(getContext(), "en");
K9ActivityCommon.setLanguage(InstrumentationRegistry.getTargetContext(), "en");
Locale.setDefault(Locale.US);
TimeZone.setDefault(TimeZone.getTimeZone("GMT+01:00"));
@ -140,7 +151,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase {
MimeMessageHelper.setBody(message, multipart);
// Extract text
ViewableContainer container = extractTextAndAttachments(getContext(), message);
ViewableContainer container = extractTextAndAttachments(InstrumentationRegistry.getTargetContext(), message);
String expectedText =
bodyText +

View File

@ -1894,9 +1894,15 @@ public class Account implements BaseAccount, StoreConfig {
public void deleteCertificates() {
LocalKeyStore localKeyStore = LocalKeyStore.getInstance();
Uri uri = Uri.parse(getStoreUri());
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort());
uri = Uri.parse(getTransportUri());
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort());
String storeUri = getStoreUri();
if (storeUri != null) {
Uri uri = Uri.parse(storeUri);
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.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
@ -87,7 +86,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
setContentView(R.layout.account_setup_check_settings);
mMessageView = (TextView)findViewById(R.id.message);
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);
mProgressBar.setIndeterminate(true);
@ -96,82 +95,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
mDirection = (CheckDirection) getIntent().getSerializableExtra(EXTRA_CHECK_DIRECTION);
new Thread() {
@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();
new CheckAccountTask(mAccount).execute(mDirection);
}
private void handleCertificateValidationException(CertificateValidationException cve) {
@ -199,14 +123,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
}
private void setMessage(final int resId) {
mHandler.post(new Runnable() {
public void run() {
if (mDestroyed) {
return;
}
mMessageView.setText(getString(resId));
}
});
mMessageView.setText(getString(resId));
}
private void acceptKeyDialog(final int msgResId, final CertificateValidationException ex) {
@ -266,7 +183,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
for (List<?> subjectAlternativeName : subjectAlternativeNames) {
Integer type = (Integer)subjectAlternativeName.get(0);
Object value = subjectAlternativeName.get(1);
String name = "";
String name;
switch (type.intValue()) {
case 0:
Log.w(K9.LOG_TAG, "SubjectAltName of type OtherName not supported.");
@ -473,4 +390,89 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
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.R;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.HtmlSanitizer;
public class MessageWebView extends RigidWebView {
@ -123,7 +125,9 @@ public class MessageWebView extends RigidWebView {
}
content += HtmlConverter.cssStylePre();
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();
}

View File

@ -129,6 +129,10 @@
<incoming uri="imap+ssl+://imap.gmx.com" username="$email" />
<outgoing uri="smtp+ssl+://mail.gmx.com" username="$email" />
</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 -->
<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));
}
}