//# 1 errors, 367 messages
//#
/*
    //#CertificateManager.java:1:1: class: com.dmdirc.CertificateManager$1
    //#CertificateManager.java:1:1: class: com.dmdirc.CertificateManager$CertificateNotTrustedException
    //#CertificateManager.java:1:1: method: com.dmdirc.CertificateManager$CertificateNotTrustedException.com.dmdirc.CertificateManager$CertificateNotTrustedException__static_init
    //#CertificateManager.java:1:1: class: com.dmdirc.CertificateManager$CertificateDoesntMatchHostException
    //#CertificateManager.java:1:1: method: com.dmdirc.CertificateManager$CertificateDoesntMatchHostException.com.dmdirc.CertificateManager$CertificateDoesntMatchHostException__static_init
    //#CertificateManager.java:1:1: class: com.dmdirc.CertificateManager
    //#CertificateManager.java:1:1: method: com.dmdirc.CertificateManager.com.dmdirc.CertificateManager__static_init
 * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.dmdirc;

import com.dmdirc.config.ConfigManager;
import com.dmdirc.config.IdentityManager;
import com.dmdirc.logger.ErrorLevel;
import com.dmdirc.logger.Logger;
import com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction;
import com.dmdirc.ui.core.dialogs.sslcertificate.SSLCertificateDialogModel;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.X509TrustManager;
import net.miginfocom.Base64;

/**
 * Manages storage and validation of certificates used when connecting to
 * SSL servers.
 *
 * @since 0.6.3m1
 * @author chris
 */
public class CertificateManager implements X509TrustManager {

    /** The password for the global java cacert file. */
    private final String cacertpass;

    /** The server name the user is trying to connect to. */
    private final String serverName;

    /** The configuration manager to use for settings. */
    private final ConfigManager config;

    /** The set of CAs from the global cacert file. */
    private Set<X509Certificate> globalTrustedCAs = new HashSet<X509Certificate>();

    /** Whether or not to check specified parts of the certificate. */
    private boolean checkDate, checkIssuer, checkHost;

    /** Used to synchronise the manager with the certificate dialog. */
    private final Semaphore actionSem = new Semaphore(0);
    
    /** The action to perform. */
    private CertificateAction action;

    /**
     * Creates a new certificate manager for a client connecting to the
     * specified server.
     *
     * @param serverName The name the user used to connect to the server
     * @param config The configuration manager to use
     */
    public CertificateManager(final String serverName, final ConfigManager config) {
    //#CertificateManager.java:99: method: void com.dmdirc.CertificateManager.com.dmdirc.CertificateManager(String, ConfigManager)
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): __Descendant_Table[com/dmdirc/CertificateManager]
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): __Descendant_Table[others]
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): __Dispatch_Table.loadTrustedCAs()V
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): com.dmdirc.logger.ErrorLevel.MEDIUM
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): config
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): java.io.File.separatorChar
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): serverName
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): this
    //#input(void com.dmdirc.CertificateManager(String, ConfigManager)): this.__Tag
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): new HashSet(CertificateManager#1) num objects
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): new Semaphore(CertificateManager#2) num objects
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.actionSem
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.cacertpass
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.checkDate
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.checkHost
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.checkIssuer
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.config
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.globalTrustedCAs
    //#output(void com.dmdirc.CertificateManager(String, ConfigManager)): this.serverName
    //#new obj(void com.dmdirc.CertificateManager(String, ConfigManager)): new HashSet(CertificateManager#1)
    //#new obj(void com.dmdirc.CertificateManager(String, ConfigManager)): new Semaphore(CertificateManager#2)
    //#pre[1] (void com.dmdirc.CertificateManager(String, ConfigManager)): config != null
    //#pre[4] (void com.dmdirc.CertificateManager(String, ConfigManager)): this.__Tag == com/dmdirc/CertificateManager
    //#presumption(void com.dmdirc.CertificateManager(String, ConfigManager)): com.dmdirc.config.ConfigManager:getOption(...)@102 != null
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): this.actionSem == &new Semaphore(CertificateManager#2)
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): this.cacertpass != null
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): init'ed(this.checkDate)
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): init'ed(this.checkHost)
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): init'ed(this.checkIssuer)
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): this.config == config
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): this.config != null
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): this.globalTrustedCAs == &new HashSet(CertificateManager#1)
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): this.serverName == serverName
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): init'ed(this.serverName)
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): new HashSet(CertificateManager#1) num objects == 1
    //#post(void com.dmdirc.CertificateManager(String, ConfigManager)): new Semaphore(CertificateManager#2) num objects == 1
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.util.Iterator:hasNext
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.util.Iterator:next
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.Throwable:__curr_excep_obj
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.String:toCharArray
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.io.FileInputStream
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.security.KeyStore:getInstance
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.security.KeyStore:load
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.io.FileInputStream:close
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:com.dmdirc.logger.Logger:appError
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.util.Set:iterator
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.StringBuilder
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.System:getProperty
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.StringBuilder:append
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.String:replace
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.lang.StringBuilder:toString
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.security.KeyStore:getDefaultType
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.security.cert.PKIXParameters
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.security.cert.PKIXParameters:getTrustAnchors
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.security.cert.TrustAnchor:getTrustedCert
    //#unanalyzed(void com.dmdirc.CertificateManager(String, ConfigManager)): Effects-of-calling:java.util.Set:add
        this.serverName = serverName;
        this.config = config;
        this.cacertpass = config.getOption("ssl", "cacertpass");
    //#CertificateManager.java:102: Warning: method not available - call not analyzed
    //#    call on String com.dmdirc.config.ConfigManager:getOption(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void com.dmdirc.CertificateManager(String, ConfigManager)
    //#    unanalyzed callee: String com.dmdirc.config.ConfigManager:getOption(String, String)
        this.checkDate = config.getOptionBool("ssl", "checkdate");
    //#CertificateManager.java:103: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:getOptionBool(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void com.dmdirc.CertificateManager(String, ConfigManager)
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:getOptionBool(String, String)
        this.checkIssuer = config.getOptionBool("ssl", "checkissuer");
    //#CertificateManager.java:104: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:getOptionBool(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void com.dmdirc.CertificateManager(String, ConfigManager)
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:getOptionBool(String, String)
        this.checkHost = config.getOptionBool("ssl", "checkhost");
    //#CertificateManager.java:105: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:getOptionBool(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void com.dmdirc.CertificateManager(String, ConfigManager)
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:getOptionBool(String, String)

        loadTrustedCAs();
    }
    //#CertificateManager.java:108: end of method: void com.dmdirc.CertificateManager.com.dmdirc.CertificateManager(String, ConfigManager)

    /**
     * Loads the trusted CA certificates from the Java cacerts store.
     */
    protected void loadTrustedCAs() {
        FileInputStream is = null;
    //#CertificateManager.java:114: method: void com.dmdirc.CertificateManager.loadTrustedCAs()
    //#input(void loadTrustedCAs()): com.dmdirc.logger.ErrorLevel.MEDIUM
    //#input(void loadTrustedCAs()): java.io.File.separatorChar
    //#input(void loadTrustedCAs()): this
    //#input(void loadTrustedCAs()): this.cacertpass
    //#input(void loadTrustedCAs()): this.globalTrustedCAs
    //#pre[2] (void loadTrustedCAs()): (soft) this.cacertpass != null
    //#pre[3] (void loadTrustedCAs()): (soft) this.globalTrustedCAs != null
    //#presumption(void loadTrustedCAs()): init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
    //#presumption(void loadTrustedCAs()): init'ed(java.io.File.separatorChar)
    //#presumption(void loadTrustedCAs()): java.security.KeyStore:getInstance(...)@120 != null
    //#presumption(void loadTrustedCAs()): java.security.cert.PKIXParameters:getTrustAnchors(...)@124 != null
    //#presumption(void loadTrustedCAs()): java.util.Iterator:next(...)@124 != null

        try {
            final String filename = System.getProperty("java.home")
                + "/lib/security/cacerts".replace('/', File.separatorChar);
            is = new FileInputStream(filename);
            final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            keystore.load(is, cacertpass.toCharArray());

            final PKIXParameters params = new PKIXParameters(keystore);
            for (TrustAnchor anchor : params.getTrustAnchors()) {
                globalTrustedCAs.add(anchor.getTrustedCert());
            }
        } catch (CertificateException ex) {
            Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
    //#CertificateManager.java:128: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void loadTrustedCAs()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
        } catch (IOException ex) {
            Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
    //#CertificateManager.java:130: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void loadTrustedCAs()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
        } catch (InvalidAlgorithmParameterException ex) {
            Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
    //#CertificateManager.java:132: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void loadTrustedCAs()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
        } catch (KeyStoreException ex) {
            Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
    //#CertificateManager.java:134: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void loadTrustedCAs()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
        } catch (NoSuchAlgorithmException ex) {
            Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
    //#CertificateManager.java:136: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void loadTrustedCAs()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
    //#CertificateManager.java:141: Warning: unused assignment
    //#    Unused assignment into ex
    //#    severity: LOW
    //#    class: com.dmdirc.CertificateManager
    //#    method: void loadTrustedCAs()
                    // ...
                }
            }
        }
    }
    //#CertificateManager.java:146: end of method: void com.dmdirc.CertificateManager.loadTrustedCAs()

    /**
     * Retrieves a KeyManager[] for the client certicate specified in the
     * configuration, if there is one.
     *
     * @return A KeyManager to use for the SSL connection
     */
    public KeyManager[] getKeyManager() {
        if (config.hasOptionString("ssl", "clientcert.file")) {
    //#CertificateManager.java:155: method: KeyManager[] com.dmdirc.CertificateManager.getKeyManager()
    //#CertificateManager.java:155: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#input(KeyManager[] getKeyManager()): com.dmdirc.logger.ErrorLevel.MEDIUM
    //#input(KeyManager[] getKeyManager()): this
    //#input(KeyManager[] getKeyManager()): this.config
    //#output(KeyManager[] getKeyManager()): return_value
    //#pre[2] (KeyManager[] getKeyManager()): this.config != null
    //#presumption(KeyManager[] getKeyManager()): com.dmdirc.config.ConfigManager:getOption(...)@161 != null
    //#presumption(KeyManager[] getKeyManager()): init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
    //#presumption(KeyManager[] getKeyManager()): java.security.KeyStore:getInstance(...)@167 != null
    //#presumption(KeyManager[] getKeyManager()): javax.net.ssl.KeyManagerFactory:getInstance(...)@170 != null
    //#post(KeyManager[] getKeyManager()): init'ed(return_value)
    //#test_vector(KeyManager[] getKeyManager()): com.dmdirc.config.ConfigManager:hasOptionString(...)@155: {0}, {1}
    //#test_vector(KeyManager[] getKeyManager()): com.dmdirc.config.ConfigManager:hasOptionString(...)@160: {0}, {1}
            FileInputStream fis = null;
            try {
                final char[] pass;

                if (config.hasOptionString("ssl", "clientcert.pass")) {
    //#CertificateManager.java:160: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
                    pass = config.getOption("ssl", "clientcert.pass").toCharArray();
    //#CertificateManager.java:161: Warning: method not available - call not analyzed
    //#    call on String com.dmdirc.config.ConfigManager:getOption(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: String com.dmdirc.config.ConfigManager:getOption(String, String)
                } else {
                    pass = null;
                }

                fis = new FileInputStream(config.getOption("ssl", "clientcert.file"));
    //#CertificateManager.java:166: Warning: method not available - call not analyzed
    //#    call on String com.dmdirc.config.ConfigManager:getOption(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: String com.dmdirc.config.ConfigManager:getOption(String, String)
                final KeyStore ks = KeyStore.getInstance("pkcs12");
                ks.load(fis, pass);

                final KeyManagerFactory kmf = KeyManagerFactory.getInstance(
                        KeyManagerFactory.getDefaultAlgorithm());
                kmf.init(ks, pass);

                return kmf.getKeyManagers();
            } catch (KeyStoreException ex) {
                Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
    //#CertificateManager.java:176: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
            } catch (IOException ex) {
                Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
    //#CertificateManager.java:178: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
            } catch (CertificateException ex) {
                Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
    //#CertificateManager.java:180: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
            } catch (NoSuchAlgorithmException ex) {
                Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
    //#CertificateManager.java:182: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
            } catch (UnrecoverableKeyException ex) {
                Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
    //#CertificateManager.java:184: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
    //#    unanalyzed callee: void com.dmdirc.logger.Logger:appError(ErrorLevel, String, Throwable)
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException ex) {
    //#CertificateManager.java:189: Warning: unused assignment
    //#    Unused assignment into ex
    //#    severity: LOW
    //#    class: com.dmdirc.CertificateManager
    //#    method: KeyManager[] getKeyManager()
                        // ...
                    }
                }
            }
        }

        return null;
    //#CertificateManager.java:196: end of method: KeyManager[] com.dmdirc.CertificateManager.getKeyManager()
    }

    /** {@inheritDoc} */
    @Override
    public void checkClientTrusted(final X509Certificate[] chain, final String authType)
            throws CertificateException {
        throw new CertificateException("Not supported.");
    //#CertificateManager.java:203: method: void com.dmdirc.CertificateManager.checkClientTrusted(X509Certificate[], String)
    //#CertificateManager.java:203: ?check always fails: method always ends with an exception: void checkClientTrusted(X509Certificate[], String).
    //#CertificateManager.java:203: end of method: void com.dmdirc.CertificateManager.checkClientTrusted(X509Certificate[], String)
    }

    /**
     * Determines if the specified certificate is trusted by the user.
     *
     * @param certificate The certificate to be checked
     * @return True if the certificate matches one in the trusted certificate
     * store, or if the certificate's details are marked as trusted in the
     * DMDirc configuration file.
     */
    public boolean isTrusted(final X509Certificate certificate) {
        try {
            final String sig = Base64.encodeToString(certificate.getSignature(), false);
    //#CertificateManager.java:216: method: bool com.dmdirc.CertificateManager.isTrusted(X509Certificate)
    //#CertificateManager.java:216: Warning: method not available - call not analyzed
    //#    call on String net.miginfocom.Base64:encodeToString(byte[], bool)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: bool isTrusted(X509Certificate)
    //#    unanalyzed callee: String net.miginfocom.Base64:encodeToString(byte[], bool)
    //#input(bool isTrusted(X509Certificate)): certificate
    //#input(bool isTrusted(X509Certificate)): this
    //#input(bool isTrusted(X509Certificate)): this.config
    //#input(bool isTrusted(X509Certificate)): this.globalTrustedCAs
    //#output(bool isTrusted(X509Certificate)): return_value
    //#pre[1] (bool isTrusted(X509Certificate)): (soft) certificate != null
    //#pre[3] (bool isTrusted(X509Certificate)): (soft) this.config != null
    //#pre[4] (bool isTrusted(X509Certificate)): (soft) this.globalTrustedCAs != null
    //#presumption(bool isTrusted(X509Certificate)): com.dmdirc.config.ConfigManager:getOptionList(...)@218 != null
    //#presumption(bool isTrusted(X509Certificate)): java.security.Principal:getName(...)@223 != null
    //#presumption(bool isTrusted(X509Certificate)): java.security.cert.X509Certificate:getIssuerDN(...)@223 != null
    //#presumption(bool isTrusted(X509Certificate)): java.security.cert.X509Certificate:getIssuerDN(...)@223 != null
    //#presumption(bool isTrusted(X509Certificate)): java.util.Iterator:next(...)@222 != null
    //#post(bool isTrusted(X509Certificate)): init'ed(return_value)
    //#test_vector(bool isTrusted(X509Certificate)): com.dmdirc.config.ConfigManager:hasOptionString(...)@218: {0}, {1}
    //#test_vector(bool isTrusted(X509Certificate)): java.lang.String:equals(...)@223: {0}, {1}
    //#test_vector(bool isTrusted(X509Certificate)): java.util.Arrays:equals(...)@223: {0}, {1}
    //#test_vector(bool isTrusted(X509Certificate)): java.util.Iterator:hasNext(...)@222: {0}, {1}
    //#test_vector(bool isTrusted(X509Certificate)): java.util.List:contains(...)@218: {0}, {1}

            if (config.hasOptionString("ssl", "trusted") && config.getOptionList("ssl",
    //#CertificateManager.java:218: Warning: method not available - call not analyzed
    //#    call on bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: bool isTrusted(X509Certificate)
    //#    unanalyzed callee: bool com.dmdirc.config.ConfigManager:hasOptionString(String, String)
    //#CertificateManager.java:218: Warning: method not available - call not analyzed
    //#    call on List com.dmdirc.config.ConfigManager:getOptionList(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: bool isTrusted(X509Certificate)
    //#    unanalyzed callee: List com.dmdirc.config.ConfigManager:getOptionList(String, String)
                    "trusted").contains(sig)) {
                return true;
            } else {
                for (X509Certificate trustedCert : globalTrustedCAs) {
                    if (Arrays.equals(certificate.getSignature(), trustedCert.getSignature())
                            && certificate.getIssuerDN().getName()
                            .equals(trustedCert.getIssuerDN().getName())) {
                        certificate.verify(trustedCert.getPublicKey());
                        return true;
                    }
                }
            }
        } catch (Exception ex) {
           return false;
        }

        return false;
    //#CertificateManager.java:235: end of method: bool com.dmdirc.CertificateManager.isTrusted(X509Certificate)
    }

    public boolean isValidHost(final X509Certificate certificate) {
        final Map<String, String> fields = getDNFieldsFromCert(certificate);
    //#CertificateManager.java:239: method: bool com.dmdirc.CertificateManager.isValidHost(X509Certificate)
    //#input(bool isValidHost(X509Certificate)): certificate
    //#input(bool isValidHost(X509Certificate)): this
    //#input(bool isValidHost(X509Certificate)): this.serverName
    //#output(bool isValidHost(X509Certificate)): return_value
    //#pre[1] (bool isValidHost(X509Certificate)): (soft) certificate != null
    //#presumption(bool isValidHost(X509Certificate)): java.security.cert.X509Certificate:getSubjectAlternativeNames(...)@246 != null
    //#presumption(bool isValidHost(X509Certificate)): java.util.Iterator:next(...)@246 != null
    //#presumption(bool isValidHost(X509Certificate)): java.util.List:get(...)@247 != null
    //#presumption(bool isValidHost(X509Certificate)): java.util.List:get(...)@250 != null
    //#presumption(bool isValidHost(X509Certificate)): java.util.Map:get(...)@240 != null
    //#post(bool isValidHost(X509Certificate)): init'ed(return_value)
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.util.HashMap
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.security.cert.X509Certificate:getSubjectX500Principal
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:javax.security.auth.x500.X500Principal:getName
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:javax.naming.ldap.LdapName
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:javax.naming.ldap.LdapName:getRdns
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.util.List:iterator
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.util.Iterator:hasNext
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.util.Iterator:next
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:javax.naming.ldap.Rdn:getType
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:javax.naming.ldap.Rdn:getValue
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.lang.Object:toString
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.util.Map:put
    //#unanalyzed(bool isValidHost(X509Certificate)): Effects-of-calling:java.lang.Throwable:__curr_excep_obj
    //#test_vector(bool isValidHost(X509Certificate)): java.lang.Integer:intValue(...)@247: {-2_147_483_648..1, 3..6, 8..4_294_967_295}, {7}
    //#test_vector(bool isValidHost(X509Certificate)): java.lang.Object:equals(...)@250: {0}, {1}
    //#test_vector(bool isValidHost(X509Certificate)): java.lang.String:equals(...)@240: {0}, {1}
    //#test_vector(bool isValidHost(X509Certificate)): java.security.cert.X509Certificate:getSubjectAlternativeNames(...)@245: Addr_Set{null}, Inverse{null}
    //#test_vector(bool isValidHost(X509Certificate)): java.util.Iterator:hasNext(...)@246: {0}, {1}
    //#test_vector(bool isValidHost(X509Certificate)): java.util.Map:containsKey(...)@240: {0}, {1}
        if (fields.containsKey("CN") && fields.get("CN").equals(serverName)) {
            return true;
        }

        try {
            if (certificate.getSubjectAlternativeNames() != null) {
                for (List<?> entry : certificate.getSubjectAlternativeNames()) {
                    final int type = ((Integer) entry.get(0)).intValue();

                    // DNS or IP
                    if ((type == 2 || type == 7) && entry.get(1).equals(serverName)) {
                        return true;
                    }
                }
            }
        } catch (CertificateParsingException ex) {
            return false;
        }

        return false;
    //#CertificateManager.java:259: end of method: bool com.dmdirc.CertificateManager.isValidHost(X509Certificate)
    }

    /** {@inheritDoc} */
    @Override
    public void checkServerTrusted(final X509Certificate[] chain, final String authType)
            throws CertificateException {
        final List<CertificateException> problems = new ArrayList<CertificateException>();
    //#CertificateManager.java:266: method: void com.dmdirc.CertificateManager.checkServerTrusted(X509Certificate[], String)
    //#CertificateManager.java:266: Warning: suspicious precondition
    //#    The precondition for com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1)[0..4_294_967_295] is not a contiguous range of values
    //#    severity: SUPPRESSED
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    suspicious precondition index: [5]
    //#    Attribs:  Soft
    //#input(void checkServerTrusted(X509Certificate[], String)): "Certificate was not issued to "._tainted
    //#input(void checkServerTrusted(X509Certificate[], String)): __Descendant_Table[com/dmdirc/CertificateManager]
    //#input(void checkServerTrusted(X509Certificate[], String)): __Descendant_Table[others]
    //#input(void checkServerTrusted(X509Certificate[], String)): __Dispatch_Table.isTrusted(Ljava/security/cert/X509Certificate;)Z
    //#input(void checkServerTrusted(X509Certificate[], String)): __Dispatch_Table.isValidHost(Ljava/security/cert/X509Certificate;)Z
    //#input(void checkServerTrusted(X509Certificate[], String)): chain
    //#input(void checkServerTrusted(X509Certificate[], String)): chain.length
    //#input(void checkServerTrusted(X509Certificate[], String)): chain[0]
    //#input(void checkServerTrusted(X509Certificate[], String)): chain[1..4_294_967_295]
    //#input(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1).length
    //#input(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1)[0..4_294_967_295]
    //#input(void checkServerTrusted(X509Certificate[], String)): com/dmdirc/CertificateManager$1.$SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction
    //#input(void checkServerTrusted(X509Certificate[], String)): com/dmdirc/Main.controller
    //#input(void checkServerTrusted(X509Certificate[], String)): this
    //#input(void checkServerTrusted(X509Certificate[], String)): this.__Tag
    //#input(void checkServerTrusted(X509Certificate[], String)): this.action
    //#input(void checkServerTrusted(X509Certificate[], String)): this.actionSem
    //#input(void checkServerTrusted(X509Certificate[], String)): this.checkDate
    //#input(void checkServerTrusted(X509Certificate[], String)): this.checkHost
    //#input(void checkServerTrusted(X509Certificate[], String)): this.checkIssuer
    //#input(void checkServerTrusted(X509Certificate[], String)): this.config
    //#input(void checkServerTrusted(X509Certificate[], String)): this.globalTrustedCAs
    //#input(void checkServerTrusted(X509Certificate[], String)): this.serverName
    //#input(void checkServerTrusted(X509Certificate[], String)): this.serverName._tainted
    //#pre[1] (void checkServerTrusted(X509Certificate[], String)): chain != null
    //#pre[12] (void checkServerTrusted(X509Certificate[], String)): init'ed(this.checkHost)
    //#pre[2] (void checkServerTrusted(X509Certificate[], String)): (soft) chain.length in {1..4_294_967_295}
    //#pre[3] (void checkServerTrusted(X509Certificate[], String)): (soft) chain[0] != null
    //#pre[4] (void checkServerTrusted(X509Certificate[], String)): (soft) chain[1..4_294_967_295] != null
    //#pre[5] (void checkServerTrusted(X509Certificate[], String)): (soft) com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1)[0..4_294_967_295] != 1
    //#pre[6] (void checkServerTrusted(X509Certificate[], String)): (soft) com/dmdirc/Main.controller != null
    //#pre[8] (void checkServerTrusted(X509Certificate[], String)): (soft) this.__Tag == com/dmdirc/CertificateManager
    //#pre[9] (void checkServerTrusted(X509Certificate[], String)): (soft) this.action != null
    //#pre[10] (void checkServerTrusted(X509Certificate[], String)): (soft) this.actionSem != null
    //#pre[11] (void checkServerTrusted(X509Certificate[], String)): (soft) init'ed(this.checkDate)
    //#pre[13] (void checkServerTrusted(X509Certificate[], String)): (soft) init'ed(this.checkIssuer)
    //#pre[14] (void checkServerTrusted(X509Certificate[], String)): (soft) this.config != null
    //#pre[15] (void checkServerTrusted(X509Certificate[], String)): (soft) this.globalTrustedCAs != null
    //#presumption(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.config.IdentityManager:getConfigIdentity(...)@319 != null
    //#presumption(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 >= 0
    //#presumption(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length >= 1
    //#presumption(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 < com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.HashMap
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.X509Certificate:getSubjectX500Principal
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:javax.security.auth.x500.X500Principal:getName
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:javax.naming.ldap.LdapName
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:javax.naming.ldap.LdapName:getRdns
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.List:iterator
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Iterator:hasNext
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Iterator:next
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:javax.naming.ldap.Rdn:getType
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:javax.naming.ldap.Rdn:getValue
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.lang.Object:toString
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Map:put
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.lang.Throwable:__curr_excep_obj
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Map:containsKey
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Map:get
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.lang.String:equals
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.X509Certificate:getSubjectAlternativeNames
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Collection:iterator
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.List:get
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.lang.Integer:intValue
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.lang.Object:equals
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:com.dmdirc.config.ConfigManager:hasOptionString
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.X509Certificate:getSignature
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:net.miginfocom.Base64:encodeToString
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:com.dmdirc.config.ConfigManager:getOptionList
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.List:contains
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Set:iterator
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.util.Arrays:equals
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.X509Certificate:getIssuerDN
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.Principal:getName
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.X509Certificate:getPublicKey
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.X509Certificate:verify
    //#unanalyzed(void checkServerTrusted(X509Certificate[], String)): Effects-of-calling:java.security.cert.CertificateException
    //#test_vector(void checkServerTrusted(X509Certificate[], String)): com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1)[0..4_294_967_295]: {2}, {-2_147_483_648..0, 3..4_294_967_295}
    //#test_vector(void checkServerTrusted(X509Certificate[], String)): this.checkDate: {0}, {1}
    //#test_vector(void checkServerTrusted(X509Certificate[], String)): this.checkHost: {0}, {1}
    //#test_vector(void checkServerTrusted(X509Certificate[], String)): this.checkIssuer: {0}, {1}
    //#test_vector(void checkServerTrusted(X509Certificate[], String)): java.util.List:isEmpty(...)@302: {1}, {0}
        boolean verified = false;

        if (checkHost) {
            // Check that the cert is issued to the correct host
            verified = isValidHost(chain[0]);

            if (!verified) {
                problems.add(new CertificateDoesntMatchHostException(
                        "Certificate was not issued to " + serverName));
            }

            verified = false;
        }

        for (X509Certificate cert : chain) {
            if (checkDate) {
                // Check that the certificate is in-date
                try {
                    cert.checkValidity();
                } catch (CertificateException ex) {
                    problems.add(ex);
                }
            }

            if (checkIssuer) {
                // Check that we trust an issuer

                verified |= isTrusted(cert);
            }
        }

        if (!verified && checkIssuer) {
            problems.add(new CertificateNotTrustedException("Issuer is not trusted"));
        }

        if (!problems.isEmpty()) {
            final SSLCertificateDialogModel test = new SSLCertificateDialogModel(chain, problems, this);
    //#CertificateManager.java:303: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.ui.core.dialogs.sslcertificate.SSLCertificateDialogModel(X509Certificate[], List, CertificateManager)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: void com.dmdirc.ui.core.dialogs.sslcertificate.SSLCertificateDialogModel(X509Certificate[], List, CertificateManager)
            Main.getUI().showSSLCertificateDialog(test);
    //#CertificateManager.java:304: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.ui.interfaces.UIController:showSSLCertificateDialog(SSLCertificateDialogModel)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: void com.dmdirc.ui.interfaces.UIController:showSSLCertificateDialog(SSLCertificateDialogModel)

            try {
                actionSem.acquire();
            } catch (InterruptedException ie) {
              throw new CertificateException("Thread aborted, ");
            }
            
            switch (action) {
    //#CertificateManager.java:312: method: com.dmdirc.CertificateManager$1.com.dmdirc.CertificateManager$1__static_init
    //#CertificateManager.java:312: Warning: method not available - call not analyzed
    //#    call on CertificateAction[] com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager$1
    //#    method: com.dmdirc.CertificateManager$1__static_init
    //#    unanalyzed callee: CertificateAction[] com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values()
    //#CertificateManager.java:312: Warning: method not available - call not analyzed
    //#    call on int com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager$1
    //#    method: com.dmdirc.CertificateManager$1__static_init
    //#    unanalyzed callee: int com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal()
    //#input(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.DISCONNECT
    //#input(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.IGNORE_PERMANENTY
    //#input(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.IGNORE_TEMPORARILY
    //#output(com.dmdirc.CertificateManager$1__static_init): $SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction
    //#output(com.dmdirc.CertificateManager$1__static_init): __Descendant_Table[com/dmdirc/CertificateManager$1]
    //#output(com.dmdirc.CertificateManager$1__static_init): new int[](CertificateManager$1__static_init#1) num objects
    //#output(com.dmdirc.CertificateManager$1__static_init): $SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction.length
    //#output(com.dmdirc.CertificateManager$1__static_init): $SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction[0..4_294_967_295]
    //#new obj(com.dmdirc.CertificateManager$1__static_init): new int[](CertificateManager$1__static_init#1)
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.DISCONNECT != null
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.IGNORE_PERMANENTY != null
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.IGNORE_TEMPORARILY != null
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 >= 0
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 >= 0
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 >= 0
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 < com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length@312
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 < com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length@312
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 < com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length@312
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length@312 >= 1
    //#presumption(com.dmdirc.CertificateManager$1__static_init): com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...)@312 != null
    //#post(com.dmdirc.CertificateManager$1__static_init): $SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction == &new int[](CertificateManager$1__static_init#1)
    //#post(com.dmdirc.CertificateManager$1__static_init): __Descendant_Table[com/dmdirc/CertificateManager$1] == &__Dispatch_Table
    //#post(com.dmdirc.CertificateManager$1__static_init): new int[](CertificateManager$1__static_init#1) num objects == 1
    //#post(com.dmdirc.CertificateManager$1__static_init): $SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction.length >= 1
    //#post(com.dmdirc.CertificateManager$1__static_init): $SwitchMap$com$dmdirc$ui$core$dialogs$sslcertificate$CertificateAction[0..4_294_967_295] in {0..3}, if init'ed
    //#CertificateManager.java:312: end of method: com.dmdirc.CertificateManager$1.com.dmdirc.CertificateManager$1__static_init
    //#CertificateManager.java:312: Warning: method not available - call not analyzed
    //#    call on int com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: int com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal()
                case DISCONNECT:
                    throw new CertificateException("Not trusted");
                case IGNORE_PERMANENTY:
                    final List<String> list = new ArrayList<String>(config
    //#CertificateManager.java:316: Warning: method not available - call not analyzed
    //#    call on List com.dmdirc.config.ConfigManager:getOptionList(String, String)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: List com.dmdirc.config.ConfigManager:getOptionList(String, String)
                            .getOptionList("ssl", "trusted"));
                    list.add(Base64.encodeToString(chain[0].getSignature(), false));
    //#CertificateManager.java:318: Warning: method not available - call not analyzed
    //#    call on String net.miginfocom.Base64:encodeToString(byte[], bool)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: String net.miginfocom.Base64:encodeToString(byte[], bool)
                    IdentityManager.getConfigIdentity().setOption("ssl",
    //#CertificateManager.java:319: Warning: method not available - call not analyzed
    //#    call on Identity com.dmdirc.config.IdentityManager:getConfigIdentity()
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: Identity com.dmdirc.config.IdentityManager:getConfigIdentity()
    //#CertificateManager.java:319: Warning: method not available - call not analyzed
    //#    call on void com.dmdirc.config.Identity:setOption(String, String, List)
    //#    severity: INFORMATIONAL
    //#    class: com.dmdirc.CertificateManager
    //#    method: void checkServerTrusted(X509Certificate[], String)
    //#    unanalyzed callee: void com.dmdirc.config.Identity:setOption(String, String, List)
                            "trusted", list);
                    break;
                case IGNORE_TEMPORARILY:
                    // Do nothing, continue connecting
                    break;
            }
        }
    }
    //#CertificateManager.java:327: end of method: void com.dmdirc.CertificateManager.checkServerTrusted(X509Certificate[], String)

    /**
     * Sets the action to perform for the request that's in progress.
     *
     * @param action The action that's been selected
     */
    public void setAction(final CertificateAction action) {
        this.action = action;
    //#CertificateManager.java:335: method: void com.dmdirc.CertificateManager.setAction(CertificateAction)
    //#input(void setAction(CertificateAction)): action
    //#input(void setAction(CertificateAction)): this
    //#input(void setAction(CertificateAction)): this.actionSem
    //#output(void setAction(CertificateAction)): this.action
    //#pre[3] (void setAction(CertificateAction)): this.actionSem != null
    //#post(void setAction(CertificateAction)): this.action == action
    //#post(void setAction(CertificateAction)): init'ed(this.action)
        
        actionSem.release();
    }
    //#CertificateManager.java:338: end of method: void com.dmdirc.CertificateManager.setAction(CertificateAction)

    /**
     * Retrieves the name of the server to which the user is trying to connect.
     *
     * @return The name of the server that the user is trying to connect to
     */
    public String getServerName() {
        return serverName;
    //#CertificateManager.java:346: method: String com.dmdirc.CertificateManager.getServerName()
    //#input(String getServerName()): this
    //#input(String getServerName()): this.serverName
    //#output(String getServerName()): return_value
    //#post(String getServerName()): return_value == this.serverName
    //#post(String getServerName()): init'ed(return_value)
    //#CertificateManager.java:346: end of method: String com.dmdirc.CertificateManager.getServerName()
    }

    /**
     * Reads the fields from the subject's designated name in the specified
     * certificate.
     *
     * @param cert The certificate to read
     * @return A map of the fields in the certificate's subject's designated
     * name
     */
    public static Map<String, String> getDNFieldsFromCert(final X509Certificate cert) {
        final Map<String, String> res = new HashMap<String, String>();
    //#CertificateManager.java:358: method: Map com.dmdirc.CertificateManager.getDNFieldsFromCert(X509Certificate)
    //#input(Map getDNFieldsFromCert(X509Certificate)): cert
    //#output(Map getDNFieldsFromCert(X509Certificate)): new HashMap(getDNFieldsFromCert#1) num objects
    //#output(Map getDNFieldsFromCert(X509Certificate)): return_value
    //#new obj(Map getDNFieldsFromCert(X509Certificate)): new HashMap(getDNFieldsFromCert#1)
    //#pre[1] (Map getDNFieldsFromCert(X509Certificate)): (soft) cert != null
    //#presumption(Map getDNFieldsFromCert(X509Certificate)): java.security.cert.X509Certificate:getSubjectX500Principal(...)@361 != null
    //#presumption(Map getDNFieldsFromCert(X509Certificate)): java.util.Iterator:next(...)@362 != null
    //#presumption(Map getDNFieldsFromCert(X509Certificate)): javax.naming.ldap.LdapName:getRdns(...)@362 != null
    //#presumption(Map getDNFieldsFromCert(X509Certificate)): javax.naming.ldap.Rdn:getValue(...)@363 != null
    //#post(Map getDNFieldsFromCert(X509Certificate)): return_value == &new HashMap(getDNFieldsFromCert#1)
    //#post(Map getDNFieldsFromCert(X509Certificate)): new HashMap(getDNFieldsFromCert#1) num objects == 1

        try {
            final LdapName name = new LdapName(cert.getSubjectX500Principal().getName());
            for (Rdn rdn : name.getRdns()) {
                res.put(rdn.getType(), rdn.getValue().toString());
            }
        } catch (InvalidNameException ex) {
            // Don't care
        }

        return res;
    //#CertificateManager.java:369: end of method: Map com.dmdirc.CertificateManager.getDNFieldsFromCert(X509Certificate)
    }

    /** {@inheritDoc} */
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return globalTrustedCAs.toArray(new X509Certificate[globalTrustedCAs.size()]);
    //#CertificateManager.java:375: method: X509Certificate[] com.dmdirc.CertificateManager.getAcceptedIssuers()
    //#input(X509Certificate[] getAcceptedIssuers()): this
    //#input(X509Certificate[] getAcceptedIssuers()): this.globalTrustedCAs
    //#output(X509Certificate[] getAcceptedIssuers()): return_value
    //#pre[2] (X509Certificate[] getAcceptedIssuers()): this.globalTrustedCAs != null
    //#presumption(X509Certificate[] getAcceptedIssuers()): java.util.Set:size(...)@375 >= 0
    //#post(X509Certificate[] getAcceptedIssuers()): init'ed(return_value)
    //#CertificateManager.java:375: end of method: X509Certificate[] com.dmdirc.CertificateManager.getAcceptedIssuers()
    }

    /**
     * An exception to indicate that the host on a certificate doesn't match
     * the host we're trying to connect to.
     */
    public static class CertificateDoesntMatchHostException extends CertificateException {

        /**
         * A version number for this class. It should be changed whenever the
         * class structure is changed (or anything else that would prevent
         * serialized objects being unserialized with the new class).
         */
        private static final long serialVersionUID = 1;

        /**
         * Creates a new CertificateDoesntMatchHostException
         *
         * @param msg A description of the problem
         */
        public CertificateDoesntMatchHostException(String msg) {
            super(msg);
    //#CertificateManager.java:397: method: void com.dmdirc.CertificateManager$CertificateDoesntMatchHostException.com.dmdirc.CertificateManager$CertificateDoesntMatchHostException(String)
    //#input(void com.dmdirc.CertificateManager$CertificateDoesntMatchHostException(String)): msg
    //#input(void com.dmdirc.CertificateManager$CertificateDoesntMatchHostException(String)): this
        }
    //#CertificateManager.java:398: end of method: void com.dmdirc.CertificateManager$CertificateDoesntMatchHostException.com.dmdirc.CertificateManager$CertificateDoesntMatchHostException(String)

    }

    /**
     * An exception to indicate that we do not trust the issuer of the
     * certificate (or the CA).
     */
    public static class CertificateNotTrustedException extends CertificateException {

        /**
         * A version number for this class. It should be changed whenever the
         * class structure is changed (or anything else that would prevent
         * serialized objects being unserialized with the new class).
         */
        private static final long serialVersionUID = 1;

        /**
         * Creates a new CertificateNotTrustedException
         *
         * @param msg A description of the problem
         */
        public CertificateNotTrustedException(String msg) {
            super(msg);
    //#CertificateManager.java:421: method: void com.dmdirc.CertificateManager$CertificateNotTrustedException.com.dmdirc.CertificateManager$CertificateNotTrustedException(String)
    //#input(void com.dmdirc.CertificateManager$CertificateNotTrustedException(String)): msg
    //#input(void com.dmdirc.CertificateManager$CertificateNotTrustedException(String)): this
        }
    //#CertificateManager.java:422: end of method: void com.dmdirc.CertificateManager$CertificateNotTrustedException.com.dmdirc.CertificateManager$CertificateNotTrustedException(String)

    }

}
    //#CertificateManager.java:: end of class: com.dmdirc.CertificateManager$1
    //#output(com.dmdirc.CertificateManager$CertificateNotTrustedException__static_init): __Descendant_Table[com/dmdirc/CertificateManager$CertificateNotTrustedException]
    //#post(com.dmdirc.CertificateManager$CertificateNotTrustedException__static_init): __Descendant_Table[com/dmdirc/CertificateManager$CertificateNotTrustedException] == &__Dispatch_Table
    //#CertificateManager.java:: end of method: com.dmdirc.CertificateManager$CertificateNotTrustedException.com.dmdirc.CertificateManager$CertificateNotTrustedException__static_init
    //#CertificateManager.java:: end of class: com.dmdirc.CertificateManager$CertificateNotTrustedException
    //#output(com.dmdirc.CertificateManager$CertificateDoesntMatchHostException__static_init): __Descendant_Table[com/dmdirc/CertificateManager$CertificateDoesntMatchHostException]
    //#post(com.dmdirc.CertificateManager$CertificateDoesntMatchHostException__static_init): __Descendant_Table[com/dmdirc/CertificateManager$CertificateDoesntMatchHostException] == &__Dispatch_Table
    //#CertificateManager.java:: end of method: com.dmdirc.CertificateManager$CertificateDoesntMatchHostException.com.dmdirc.CertificateManager$CertificateDoesntMatchHostException__static_init
    //#CertificateManager.java:: end of class: com.dmdirc.CertificateManager$CertificateDoesntMatchHostException
    //#output(com.dmdirc.CertificateManager__static_init): __Descendant_Table[com/dmdirc/CertificateManager]
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.getAcceptedIssuers()[Ljava/security/cert/X509Certificate;
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.getKeyManager()[Ljavax/net/ssl/KeyManager;
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.getServerName()Ljava/lang/String;
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.isTrusted(Ljava/security/cert/X509Certificate;)Z
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.isValidHost(Ljava/security/cert/X509Certificate;)Z
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.loadTrustedCAs()V
    //#output(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.setAction(Lcom/dmdirc/ui/core/dialogs/sslcertificate/CertificateAction;)V
    //#post(com.dmdirc.CertificateManager__static_init): __Descendant_Table[com/dmdirc/CertificateManager] == &__Dispatch_Table
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V == &checkClientTrusted
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V == &checkServerTrusted
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.getAcceptedIssuers()[Ljava/security/cert/X509Certificate; == &getAcceptedIssuers
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.getKeyManager()[Ljavax/net/ssl/KeyManager; == &getKeyManager
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.getServerName()Ljava/lang/String; == &getServerName
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.isTrusted(Ljava/security/cert/X509Certificate;)Z == &isTrusted
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.isValidHost(Ljava/security/cert/X509Certificate;)Z == &isValidHost
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.loadTrustedCAs()V == &loadTrustedCAs
    //#post(com.dmdirc.CertificateManager__static_init): __Dispatch_Table.setAction(Lcom/dmdirc/ui/core/dialogs/sslcertificate/CertificateAction;)V == &setAction
    //#CertificateManager.java:: end of method: com.dmdirc.CertificateManager.com.dmdirc.CertificateManager__static_init
    //#CertificateManager.java:: end of class: com.dmdirc.CertificateManager
