diff --git a/res/values-de-rDE/strings.xml b/res/values-de-rDE/strings.xml
index 16c44d392..b2155986c 100644
--- a/res/values-de-rDE/strings.xml
+++ b/res/values-de-rDE/strings.xml
@@ -192,4 +192,8 @@
Verbindungsfehler
Senden\u2026
Details anzeigen/ausblenden
+ Unrecognized Certificate
+ Accept Key
+ Reject Key
+
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 162697bb5..56768baa5 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -191,5 +191,9 @@
Reintentar la carga de más mensajes
Error de conexión
Enviando\u2026
- Ver/ocultar detalles
+ Ver/ocultar detalles
+ Unrecognized Certificate
+ Accept Key
+ Reject Key
+
diff --git a/res/values-fr-rFR/strings.xml b/res/values-fr-rFR/strings.xml
index 2c67c3d57..9330d9fef 100644
--- a/res/values-fr-rFR/strings.xml
+++ b/res/values-fr-rFR/strings.xml
@@ -191,5 +191,8 @@
Ressayer le chargement de plus de messages
Erreur de connexion
Envoi\u2026
- Afficher/masquer les détails
+ Afficher/masquer les détails
+ Unrecognized Certificate
+ Accept Key
+ Reject Key
diff --git a/res/values-it-rIT/strings.xml b/res/values-it-rIT/strings.xml
index e81bc1428..edc0c235c 100644
--- a/res/values-it-rIT/strings.xml
+++ b/res/values-it-rIT/strings.xml
@@ -192,4 +192,7 @@
Errore di connessione
Invio in corso\u2026
Visualizza/Nascondi dettagli
+ Unrecognized Certificate
+ Accept Key
+ Reject Key
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index ec9538589..caeea0838 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -192,4 +192,7 @@
連線錯誤
正在傳送\u2026
檢視/隱藏詳細資料
+ Unrecognized Certificate
+ Accept Key
+ Reject Key
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 69cb17f96..353fb0a12 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -248,9 +248,12 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
Send this address a copy of every outgoing message
- Signature
- Append a signature to every message you send
-
+ Signature
+ Append a signature to every message you send
+
+ Sent Items Folder
+ Save all sent messages to this folder
+
Remove
The account \"%s\" will be removed from Email.
@@ -260,4 +263,7 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
your correct email address and password, you may not have a paid
\"Plus\" account. Please launch the Web browser to gain access to
these mail accounts.
+ Unrecognized Certificate
+ Accept Key
+ Reject Key
diff --git a/src/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java b/src/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java
index ebc8a4b0a..8162355ba 100644
--- a/src/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java
+++ b/src/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java
@@ -1,6 +1,9 @@
package com.fsck.k9.activity.setup;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@@ -21,6 +24,7 @@ import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.CertificateValidationException;
+import com.fsck.k9.mail.store.TrustManagerFactory;
/**
* Checks the given settings to make sure that they can be used to send and
@@ -78,6 +82,7 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
new Thread() {
public void run() {
+ Store store = null;
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
if (mDestroyed) {
@@ -88,9 +93,9 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
return;
}
if (mCheckIncoming) {
- setMessage(R.string.account_setup_check_settings_check_incoming_msg);
- Store store = Store.getInstance(mAccount.getStoreUri(), getApplication());
- store.checkSettings();
+ setMessage(R.string.account_setup_check_settings_check_incoming_msg);
+ store = Store.getInstance(mAccount.getStoreUri(), getApplication());
+ store.checkSettings();
}
if (mDestroyed) {
return;
@@ -118,17 +123,19 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
} catch (final AuthenticationFailedException afe) {
showErrorDialog(
R.string.account_setup_failed_dlg_auth_message_fmt,
- afe.getMessage() == null ? "" : afe.getMessage());
+ afe.getMessage() == null ? "" : afe.getMessage());
} catch (final CertificateValidationException cve) {
- showErrorDialog(
+ acceptKeyDialog(
R.string.account_setup_failed_dlg_certificate_message_fmt,
- cve.getMessage() == null ? "" : cve.getMessage());
+ cve);
+ //cve.getMessage() == null ? "" : cve.getMessage());
} catch (final MessagingException me) {
showErrorDialog(
R.string.account_setup_failed_dlg_server_message_fmt,
me.getMessage() == null ? "" : me.getMessage());
}
}
+
}.start();
}
@@ -172,7 +179,86 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
}
});
}
+ private void acceptKeyDialog(final int msgResId, final Object... args) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (mDestroyed) {
+ return;
+ }
+ final X509Certificate[] chain = TrustManagerFactory.getLastCertChain();
+ String exMessage = "Unknown Error";
+
+ Exception ex = ((Exception)args[0]);
+ if (ex != null) {
+ if (ex.getCause() != null) {
+ if (ex.getCause().getCause() != null) {
+ exMessage = ex.getCause().getCause().getMessage();
+
+ } else {
+ exMessage = ex.getCause().getMessage();
+ }
+ } else {
+ exMessage = ex.getMessage();
+ }
+ }
+
+ mProgressBar.setIndeterminate(false);
+ StringBuffer chainInfo = new StringBuffer(100);
+ for (int i = 0; i < chain.length; i++)
+ {
+ // display certificate chain information
+ chainInfo.append("Certificate chain[" + i + "]:\n");
+ chainInfo.append("Subject: " + chain[i].getSubjectDN().toString() + "\n");
+ chainInfo.append("Issuer: " + chain[i].getIssuerDN().toString() + "\n");
+ }
+ new AlertDialog.Builder(AccountSetupCheckSettings.this)
+ .setTitle(getString(R.string.account_setup_failed_dlg_invalid_certificate_title))
+ //.setMessage(getString(R.string.account_setup_failed_dlg_invalid_certificate)
+ .setMessage(getString(msgResId,exMessage)
+ + " " + chainInfo.toString()
+ )
+ .setCancelable(true)
+ .setPositiveButton(
+ getString(R.string.account_setup_failed_dlg_invalid_certificate_accept),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ try {
+ String alias = mAccount.getUuid();
+ if (mCheckIncoming) {
+ alias = alias + ".incoming";
+ }
+ if (mCheckOutgoing) {
+ alias = alias + ".outgoing";
+ }
+ TrustManagerFactory.addCertificateChain(alias, chain);
+ } catch (CertificateException e) {
+ showErrorDialog(
+ R.string.account_setup_failed_dlg_certificate_message_fmt,
+ e.getMessage() == null ? "" : e.getMessage());
+ }
+ AccountSetupCheckSettings.actionCheckSettings(AccountSetupCheckSettings.this, mAccount,
+ mCheckIncoming, mCheckOutgoing);
+ }
+ })
+ .setNegativeButton(
+ getString(R.string.account_setup_failed_dlg_invalid_certificate_reject),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ })
+ .show();
+ }
+ });
+ }
+
+ public void onActivityResult(int reqCode, int resCode, Intent data) {
+ setResult(resCode);
+ finish();
+ }
+
+
private void onCancel() {
mCanceled = true;
setMessage(R.string.account_setup_check_settings_canceling_msg);
diff --git a/src/com/fsck/k9/k9.java b/src/com/fsck/k9/k9.java
index 12f6e02b4..ea0084a68 100644
--- a/src/com/fsck/k9/k9.java
+++ b/src/com/fsck/k9/k9.java
@@ -17,6 +17,8 @@ import com.fsck.k9.service.BootReceiver;
import com.fsck.k9.service.MailService;
public class k9 extends Application {
+ public static Application app = null;
+
public static final String LOG_TAG = "k9";
public static File tempDirectory;
@@ -64,13 +66,14 @@ public class k9 extends Application {
* The MIME type(s) of attachments we're willing to download to SD.
*/
public static final String[] ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
- "*/*",
+ "image/*",
};
/**
* The MIME type(s) of attachments we're not willing to download to SD.
*/
public static final String[] UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
+ "image/gif",
};
/**
@@ -87,7 +90,7 @@ public class k9 extends Application {
public static final int DEFAULT_VISIBLE_LIMIT = 25;
/**
- * Number of additional messages to load when a user selectes "Load more messages..."
+ * Number of additioanl messages to load when a user selectes "Load more messages..."
*/
public static final int VISIBLE_LIMIT_INCREMENT = 25;
@@ -146,6 +149,7 @@ public class k9 extends Application {
@Override
public void onCreate() {
super.onCreate();
+ app = this;
Preferences prefs = Preferences.getPreferences(this);
DEBUG = prefs.geteEnableDebugLogging();
DEBUG_SENSITIVE = prefs.getEnableSensitiveLogging();
@@ -156,13 +160,6 @@ public class k9 extends Application {
* doesn't work in Android and MimeMessage does not have access to a Context.
*/
BinaryTempFileBody.setTempDirectory(getCacheDir());
-
- /*
- * Enable background sync of messages
- */
-
- setServicesEnabled(this);
-
}
}
diff --git a/src/com/fsck/k9/mail/store/TrustManagerFactory.java b/src/com/fsck/k9/mail/store/TrustManagerFactory.java
index dcfa2070c..374e8a33f 100644
--- a/src/com/fsck/k9/mail/store/TrustManagerFactory.java
+++ b/src/com/fsck/k9/mail/store/TrustManagerFactory.java
@@ -2,8 +2,13 @@
package com.fsck.k9.mail.store;
import android.util.Log;
+import android.app.Application;
+import android.content.Context;
import android.net.http.DomainNameChecker;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.KeyStoreException;
@@ -13,11 +18,22 @@ import java.security.cert.CertificateException;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.TrustManager;
+import com.fsck.k9.k9;
+
public final class TrustManagerFactory {
private static final String LOG_TAG = "TrustManagerFactory";
- private static X509TrustManager sSecureTrustManager;
- private static X509TrustManager sUnsecureTrustManager;
+ private static X509TrustManager defaultTrustManager;
+ private static X509TrustManager unsecureTrustManager;
+ private static X509TrustManager localTrustManager;
+
+ private static SecureX509TrustManager secureTrustManager;
+
+ private static X509Certificate[] lastCertChain = null;
+
+ private static File keyStoreFile;
+ private static KeyStore keyStore;
+
private static class SimpleX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType)
@@ -34,62 +50,171 @@ public final class TrustManagerFactory {
}
private static class SecureX509TrustManager implements X509TrustManager {
- private X509TrustManager mTrustManager;
- private String mHost;
+ //private static X509TrustManager mTrustManager;
+ //private static X509TrustManager mLocalTrustManager;
+ private static String mHost;
+ private static SecureX509TrustManager me;
- SecureX509TrustManager(X509TrustManager trustManager, String host) {
- mTrustManager = trustManager;
- mHost = host;
+ private SecureX509TrustManager() {
}
+ public static X509TrustManager getInstance(String host) {
+ mHost = host;
+ if (me == null) {
+ me = new SecureX509TrustManager();
+ }
+ return me;
+ }
+
+ public static void setHost(String host){
+ mHost = host;
+ }
+
+ //
+// public static void updateTrustManager(X509TrustManager trustManager) {
+// mTrustManager = trustManager;
+// }
+
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
- mTrustManager.checkClientTrusted(chain, authType);
+ defaultTrustManager.checkClientTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
-
- mTrustManager.checkServerTrusted(chain, authType);
-
- if (!DomainNameChecker.match(chain[0], mHost)) {
- throw new CertificateException("Certificate domain name does not match "
- + mHost);
- }
+ throws CertificateException {
+ TrustManagerFactory.setLastCertChain(chain);
+ try {
+ defaultTrustManager.checkServerTrusted(chain, authType);
+ } catch (CertificateException e) {
+ localTrustManager.checkServerTrusted(chain, authType);
+ }
+ if (!DomainNameChecker.match(chain[0], mHost)) {
+ try {
+ String dn = chain[0].getSubjectDN().toString();
+ if ((dn != null) && (dn.equalsIgnoreCase(keyStore.getCertificateAlias(chain[0])))) {
+ return;
+ }
+ } catch (KeyStoreException e) {
+ throw new CertificateException("Certificate cannot be verified; KeyStore Exception: " + e);
+ }
+ throw new CertificateException("Certificate domain name does not match "
+ + mHost);
+ }
}
public X509Certificate[] getAcceptedIssuers() {
- return mTrustManager.getAcceptedIssuers();
+ return defaultTrustManager.getAcceptedIssuers();
}
+
}
static {
try {
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
- tmf.init((KeyStore) null);
+ Application app = k9.app;
+ keyStoreFile = new File(app.getDir("KeyStore", Context.MODE_PRIVATE) + File.separator + "KeyStore.bks");
+ keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ //TODO: read store from disk.
+ java.io.FileInputStream fis;
+ try {
+ fis = new java.io.FileInputStream(keyStoreFile);
+ } catch (FileNotFoundException e1) {
+ fis = null;
+ }
+ try {
+ keyStore.load(fis, "".toCharArray());
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "KeyStore IOException while initializing TrustManagerFactory ", e);
+ keyStore = null;
+ } catch (CertificateException e) {
+ Log.e(LOG_TAG, "KeyStore CertificateException while initializing TrustManagerFactory ", e);
+ keyStore = null;
+ }
+ tmf.init(keyStore);
TrustManager[] tms = tmf.getTrustManagers();
if (tms != null) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
- sSecureTrustManager = (X509TrustManager) tm;
+ localTrustManager = (X509TrustManager)tm;
break;
}
}
}
+ tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
+ tmf.init((KeyStore)null);
+ tms = tmf.getTrustManagers();
+ if (tms != null) {
+ for (TrustManager tm : tms) {
+ if (tm instanceof X509TrustManager) {
+ defaultTrustManager = (X509TrustManager) tm;
+ break;
+ }
+ }
+ }
+
} catch (NoSuchAlgorithmException e) {
Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
} catch (KeyStoreException e) {
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
}
-
- sUnsecureTrustManager = new SimpleX509TrustManager();
+ unsecureTrustManager = new SimpleX509TrustManager();
}
private TrustManagerFactory() {
}
public static X509TrustManager get(String host, boolean secure) {
- return secure ? new SecureX509TrustManager(sSecureTrustManager, host) :
- sUnsecureTrustManager;
+ return secure ? SecureX509TrustManager.getInstance(host) :
+ unsecureTrustManager;
}
+
+ public static KeyStore getKeyStore() {
+ return keyStore;
+ }
+
+ public static void setLastCertChain(X509Certificate[] chain) {
+ lastCertChain = chain;
+ }
+ public static X509Certificate[] getLastCertChain() {
+ return lastCertChain;
+ }
+
+ public static void addCertificateChain(String alias, X509Certificate[] chain) throws CertificateException {
+ try {
+ javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
+ for (int i = 0; i < chain.length; i++)
+ {
+ keyStore.setCertificateEntry
+ (chain[i].getSubjectDN().toString(), chain[i]);
+ }
+
+ tmf.init(keyStore);
+ TrustManager[] tms = tmf.getTrustManagers();
+ if (tms != null) {
+ for (TrustManager tm : tms) {
+ if (tm instanceof X509TrustManager) {
+ localTrustManager = (X509TrustManager) tm;
+ break;
+ }
+ }
+ }
+ java.io.FileOutputStream keyStoreStream;
+ try {
+ keyStoreStream = new java.io.FileOutputStream(keyStoreFile);
+ keyStore.store(keyStoreStream, "".toCharArray());
+ keyStoreStream.close();
+ } catch (FileNotFoundException e) {
+ throw new CertificateException("Unable to write KeyStore: " + e.getMessage());
+ } catch (CertificateException e) {
+ throw new CertificateException("Unable to write KeyStore: " + e.getMessage());
+ } catch (IOException e) {
+ throw new CertificateException("Unable to write KeyStore: " + e.getMessage());
+ }
+
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
+ } catch (KeyStoreException e) {
+ Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
+ }
+ }
}