
package com.fsck.k9.net.ssl;

import android.util.Log;

import com.fsck.k9.helper.DomainNameChecker;
import com.fsck.k9.mail.CertificateChainException;
import com.fsck.k9.security.LocalKeyStore;

import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

public final class TrustManagerFactory {
    private static final String LOG_TAG = "TrustManagerFactory";

    private static X509TrustManager defaultTrustManager;
    private static X509TrustManager unsecureTrustManager;

    private static LocalKeyStore keyStore;

    private static class SimpleX509TrustManager implements X509TrustManager {
        public void checkClientTrusted(X509Certificate[] chain, String authType)
        throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
        throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    private static class SecureX509TrustManager implements X509TrustManager {
        private static final Map<String, SecureX509TrustManager> mTrustManager =
            new HashMap<String, SecureX509TrustManager>();

        private final String mHost;
        private final int mPort;

        private SecureX509TrustManager(String host, int port) {
            mHost = host;
            mPort = port;
        }

        public synchronized static X509TrustManager getInstance(String host, int port) {
            String key = host + ":" + port;
            SecureX509TrustManager trustManager;
            if (mTrustManager.containsKey(key)) {
                trustManager = mTrustManager.get(key);
            } else {
                trustManager = new SecureX509TrustManager(host, port);
                mTrustManager.put(key, trustManager);
            }

            return trustManager;
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType)
        throws CertificateException {
            defaultTrustManager.checkClientTrusted(chain, authType);
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            boolean foundInGlobalKeyStore = false;
            try {
                defaultTrustManager.checkServerTrusted(chain, authType);
                foundInGlobalKeyStore = true;
            } catch (CertificateException e) { /* ignore */ }

            X509Certificate certificate = chain[0];

            // Check the local key store if we couldn't verify the certificate using the global
            // key store or if the host name doesn't match the certificate name
            if (foundInGlobalKeyStore
                    && DomainNameChecker.match(certificate, mHost)
                    || keyStore.isValidCertificate(certificate, mHost, mPort)) {
                return;
            }
            String message = (foundInGlobalKeyStore) ?
                    "Certificate domain name does not match " + mHost :
                    "Couldn't find certificate in local key store";

            throw new CertificateChainException(message, chain);
        }

        public X509Certificate[] getAcceptedIssuers() {
            return defaultTrustManager.getAcceptedIssuers();
        }

    }

    static {
        try {
            keyStore = LocalKeyStore.getInstance();

            javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
            tmf.init((KeyStore) null);

            TrustManager[] 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);
        }
        unsecureTrustManager = new SimpleX509TrustManager();
    }

    private TrustManagerFactory() {
    }

    public static X509TrustManager get(String host, int port, boolean secure) {
        return secure ? SecureX509TrustManager.getInstance(host, port) :
               unsecureTrustManager;
    }
}

@@ -2451,32 +2451,67 @@
 cateException %7B%0A
+            String message = null;%0A
             bool
@@ -2545,16 +2545,16 @@
  false;%0A
-
         
@@ -2732,21 +2732,62 @@
 e) %7B
- /* ignore */
+%0A                message = e.getMessage();%0A           
  %7D%0A%0A
@@ -3231,32 +3231,33 @@
 ;%0A            %7D%0A
+%0A
             Stri
@@ -3244,38 +3244,70 @@
  %7D%0A%0A            
-String
+if (message == null) %7B%0A               
  message = (foun
@@ -3327,16 +3327,20 @@
 tore) ?%0A
+    
         
@@ -3423,16 +3423,20 @@
         
+   
+ 
 %22Couldn'
@@ -3474,16 +3474,30 @@
  store%22;
+%0A            %7D
 %0A%0A      

Return proper error message when certificate couldn't be verified against global key store