/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 */

package org.opends.server.util.cli;

import org.opends.messages.Message;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.messages.QuickSetupMessages.*;
import static org.opends.messages.ToolMessages.*;

import org.opends.quicksetup.Step;
import org.opends.quicksetup.UserDataCertificateException;
import org.opends.quicksetup.util.Utils;
import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
import org.opends.server.tools.LDAPConnectionOptions;
import org.opends.server.tools.SSLConnectionFactory;
import org.opends.server.tools.SSLConnectionException;
import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.SelectableCertificateKeyManager;
import org.opends.admin.ads.ADSContext;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.admin.ads.util.ApplicationKeyManager;

import javax.net.ssl.KeyManager;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Supports interacting with a user through the command line to
 * prompt for information necessary to create an LDAP connection.
 */
public class LDAPConnectionConsoleInteraction {

  private boolean useSSL;
  private boolean useStartTLS;
  private String hostName;
  private int portNumber;
  private String bindDN;
  private String providedBindDN;
  private String adminUID;
  private String providedAdminUID;
  private String bindPassword;
  private KeyManager keyManager;
  private ApplicationTrustManager trustManager;
  // Boolean that tells if we ask for bind DN or admin UID in the same prompt.
  private boolean useAdminOrBindDn = false;
  // Boolean that tells if we must propose LDAP if it is available even if the
  // user provided certificate parameters.
  private boolean displayLdapIfSecureParameters = false;

  // The SecureConnectionCliArgsList object.
  private SecureConnectionCliArgs secureArgsList = null;

  // Indicate if we need to display the heading
  private boolean isHeadingDisplayed = false;

  // the Console application
  private ConsoleApplication app;

  // Indicate if the trust store in in memory
  private boolean trustStoreInMemory = false;

  // Indicate if the all certificates are accepted
  private boolean trustAll = false;

  // Indicate that the trust manager was created with the parameters provided
  private boolean trustManagerInitialized;

  // The trust store to use for the SSL or STARTTLS connection
  private KeyStore truststore;

  private String keystorePath;

  private String keystorePassword;

  private String certifNickname;

  private String truststorePath;

  private String truststorePassword;

  // The timeout to be used to connect
  private int connectTimeout;

  private Message heading = INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get();

  // A copy of the secureArgList for convenience.
  private SecureConnectionCliArgs copySecureArgsList = null;

  // The command builder that we can return with the connection information.
  private CommandBuilder commandBuilder;


  /**
   * Enumeration description protocols for interactive CLI choices.
   */
  private enum Protocols
  {
    LDAP(1, INFO_LDAP_CONN_PROMPT_SECURITY_LDAP.get()), SSL(2,
        INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL.get()), START_TLS(3,
        INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TLS.get());

    private Integer choice;

    private Message msg;

    /**
     * Private constructor.
     *
     * @param i
     *          the menu return value.
     * @param msg
     *          the message message.
     */
    private Protocols(int i, Message msg)
    {
      choice = i;
      this.msg = msg;
    }

    /**
     * Returns the choice number.
     *
     * @return the attribute name.
     */
    public Integer getChoice()
    {
      return choice;
    }

    /**
     * Return the menu message.
     *
     * @return the menu message.
     */
    public Message getMenuMessage()
    {
      return msg;
    }
  }

  /**
   * Enumeration description protocols for interactive CLI choices.
   */
  private enum TrustMethod
  {
    TRUSTALL(1, INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get()),

    TRUSTSTORE(2,INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE.get()),

    DISPLAY_CERTIFICATE(3,INFO_LDAP_CONN_PROMPT_SECURITY_MANUAL_CHECK.get());

    private Integer choice;

    private Message msg;

    /**
     * Private constructor.
     *
     * @param i
     *          the menu return value.
     * @param msg
     *          the message message.
     */
    private TrustMethod(int i, Message msg)
    {
      choice = new Integer(i);
      this.msg = msg;
    }

    /**
     * Returns the choice number.
     *
     * @return the attribute name.
     */
    public Integer getChoice()
    {
      return choice;
    }

    /**
     * Return the menu message.
     *
     * @return the menu message.
     */
    public Message getMenuMessage()
    {
      return msg;
    }
  }

  /**
   * Enumeration description server certificate trust option.
   */
  private enum TrustOption
  {
    UNTRUSTED(1, INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO.get()),
    SESSION(2,INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION.get()),
    PERMAMENT(3,INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS.get()),
    CERTIFICATE_DETAILS(4,
        INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS.get());

    private Integer choice;

    private Message msg;

    /**
     * Private constructor.
     *
     * @param i
     *          the menu return value.
     * @param msg
     *          the message message.
     */
    private TrustOption(int i, Message msg)
    {
      choice = new Integer(i);
      this.msg = msg;
    }

    /**
     * Returns the choice number.
     *
     * @return the attribute name.
     */
    public Integer getChoice()
    {
      return choice;
    }

    /**
     * Return the menu message.
     *
     * @return the menu message.
     */
    public Message getMenuMessage()
    {
      return msg;
    }
  }
  /**
   * Constructs a parameterized instance.
   *
   * @param app console application
   * @param secureArgs existing set of arguments that have already
   *        been parsed and contain some potential command line specified
   *        LDAP arguments
   */
  public LDAPConnectionConsoleInteraction(ConsoleApplication app,
                                          SecureConnectionCliArgs secureArgs) {
    this.app = app;
    this.secureArgsList = secureArgs;
    this.commandBuilder = new CommandBuilder(null);
    this.useSSL = secureArgs.useSSL();
    this.useStartTLS = secureArgs.useStartTLS();
    this.trustAll = secureArgs.trustAllArg.isPresent();
    copySecureArgsList = new SecureConnectionCliArgs(secureArgs.alwaysSSL());
    try
    {
      copySecureArgsList.createGlobalArguments();
    }
    catch (Throwable t)
    {
      // This is  a bug: we should always be able to create the global arguments
      // no need to localize this one.
      throw new RuntimeException("Unexpected error: "+t, t);
    }
  }

  /**
   * Interact with the user though the console to get information
   * necessary to establish an LDAP connection.
   *
   * @throws ArgumentException if there is a problem with the arguments
   */
  public void run()
          throws ArgumentException
  {
    run(true, true);
  }


  /**
   * Interact with the user though the console to get information
   * necessary to establish an LDAP connection.
   * @param canUseSSL whether we can propose to connect using SSL or not.
   * @param canUseStartTLS whether we can propose to connect using Start TLS or
   * not.
   *
   * @throws ArgumentException if there is a problem with the arguments
   */
  public void run(boolean canUseSSL, boolean canUseStartTLS)
          throws ArgumentException
  {
    // Reset everything
    commandBuilder.clearArguments();
    copySecureArgsList.createGlobalArguments();
    boolean secureConnection = (canUseSSL || canUseStartTLS) &&
      (
          secureArgsList.useSSLArg.isPresent()
          ||
          secureArgsList.useStartTLSArg.isPresent()
          ||
          secureArgsList.trustAllArg.isPresent()
          ||
          secureArgsList.trustStorePathArg.isPresent()
          ||
          secureArgsList.trustStorePasswordArg.isPresent()
          ||
          secureArgsList.trustStorePasswordFileArg.isPresent()
          ||
          secureArgsList.keyStorePathArg.isPresent()
          ||
          secureArgsList.keyStorePasswordArg.isPresent()
          ||
          secureArgsList.keyStorePasswordFileArg.isPresent()
      );
    // Get the LDAP host.
    hostName = secureArgsList.hostNameArg.getValue();
    final String tmpHostName = hostName;
    if (app.isInteractive() && !secureArgsList.hostNameArg.isPresent())
    {
      checkHeadingDisplayed();

      ValidationCallback<String> callback = new ValidationCallback<String>()
      {

        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return tmpHostName;
          }
          else
          {
            try
            {
              InetAddress.getByName(ninput);
              return ninput;
            }
            catch (UnknownHostException e)
            {
              // Try again...
              app.println();
              app.println(ERR_LDAP_CONN_BAD_HOST_NAME.get(ninput));
              app.println();
              return null;
            }
          }
        }

      };

      try
      {
        app.println();
        hostName = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_HOST_NAME
            .get(hostName), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }

    copySecureArgsList.hostNameArg.clearValues();
    copySecureArgsList.hostNameArg.addValue(hostName);
    commandBuilder.addArgument(copySecureArgsList.hostNameArg);

    // Connection type
    useSSL = secureArgsList.useSSL();
    useStartTLS = secureArgsList.useStartTLS();
    boolean connectionTypeIsSet =
      (
        secureArgsList.alwaysSSL()
        ||
        secureArgsList.useSSLArg.isPresent()
        ||
        secureArgsList.useStartTLSArg.isPresent()
        ||
        (
          secureArgsList.useSSLArg.isValueSetByProperty()
          &&
          secureArgsList.useStartTLSArg.isValueSetByProperty()
        )
      );
    if (app.isInteractive() && !connectionTypeIsSet)
    {
      checkHeadingDisplayed();

      MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
      builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_USE_SECURE_CTX.get());

      Protocols defaultProtocol ;
      if (secureConnection)
      {
        defaultProtocol = Protocols.SSL;
      }
      else
      {
        defaultProtocol = Protocols.LDAP;
      }
      for (Protocols p : Protocols.values())
      {
        if (secureConnection && p.equals(Protocols.LDAP) &&
            !displayLdapIfSecureParameters)
        {
          continue ;
        }
        if (!canUseSSL && p.equals(Protocols.SSL))
        {
          continue;
        }
        if (!canUseStartTLS && p.equals(Protocols.START_TLS))
        {
          continue;
        }
        int i = builder.addNumberedOption(p.getMenuMessage(), MenuResult
            .success(p.getChoice()));
        if (p.equals(defaultProtocol))
        {
          builder.setDefault(
              INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
                  .get(i), MenuResult.success(p.getChoice()));
        }
      }

      Menu<Integer> menu = builder.toMenu();
      try
      {
        MenuResult<Integer> result = menu.run();
        if (result.isSuccess())
        {
          if (result.getValue().equals(Protocols.SSL.getChoice()))
          {
            useSSL = true;
          }
          else if (result.getValue()
              .equals(Protocols.START_TLS.getChoice()))
          {
            useStartTLS = true;
          }
        }
        else
        {
          // Should never happen.
          throw new RuntimeException();
        }
      }
      catch (CLIException e)
      {
        throw new RuntimeException(e);
      }
    }

    if (useSSL)
    {
      commandBuilder.addArgument(copySecureArgsList.useSSLArg);
    }
    else if (useStartTLS)
    {
      commandBuilder.addArgument(copySecureArgsList.useStartTLSArg);
    }

    // Get the LDAP port.
    if (!useSSL)
    {
      portNumber = secureArgsList.portArg.getIntValue();
    }
    else
    {
      if (secureArgsList.portArg.isPresent())
      {
        portNumber = secureArgsList.portArg.getIntValue();
      }
      else
      {
        portNumber = secureArgsList.getPortFromConfig();
      }
    }
    final int tmpPortNumber = portNumber;
    if (app.isInteractive() && !secureArgsList.portArg.isPresent())
    {
      checkHeadingDisplayed();

      ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
      {

        public Integer validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return tmpPortNumber;
          }
          else
          {
            try
            {
              int i = Integer.parseInt(ninput);
              if (i < 1 || i > 65535)
              {
                throw new NumberFormatException();
              }
              return i;
            }
            catch (NumberFormatException e)
            {
              // Try again...
              app.println();
              app.println(ERR_LDAP_CONN_BAD_PORT_NUMBER.get(ninput));
              app.println();
              return null;
            }
          }
        }

      };

      try
      {
        app.println();
        Message askPortNumber = null;
        if (secureArgsList.alwaysSSL()) {
          askPortNumber = INFO_ADMIN_CONN_PROMPT_PORT_NUMBER.get(portNumber);
        } else {
          askPortNumber = INFO_LDAP_CONN_PROMPT_PORT_NUMBER.get(portNumber);
        }
        portNumber = app.readValidatedInput(askPortNumber, callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }

    copySecureArgsList.portArg.clearValues();
    copySecureArgsList.portArg.addValue(String.valueOf(portNumber));
    commandBuilder.addArgument(copySecureArgsList.portArg);

    // Handle certificate
    if ((useSSL || useStartTLS) && (trustManager == null))
    {
      initializeTrustManager();
    }

    // Get the LDAP bind credentials.
    bindDN = secureArgsList.bindDnArg.getValue();
    adminUID = secureArgsList.adminUidArg.getValue();
    final boolean useAdmin = secureArgsList.useAdminUID();
    if (useAdmin && secureArgsList.adminUidArg.isPresent())
    {
      providedAdminUID = adminUID;
    }
    else
    {
      providedAdminUID = null;
    }
    if ((!useAdmin || useAdminOrBindDn) &&
        secureArgsList.bindDnArg.isPresent())
    {
      providedBindDN = bindDN;
    }
    else
    {
      providedBindDN = null;
    }
    boolean argIsPresent = (providedAdminUID != null) ||
    (providedBindDN != null);
    final String tmpBindDN = bindDN;
    final String tmpAdminUID = adminUID;
    if (keyManager == null)
    {
      if (app.isInteractive() && !argIsPresent)
      {
        checkHeadingDisplayed();

        ValidationCallback<String> callback = new ValidationCallback<String>()
        {

          public String validate(ConsoleApplication app, String input)
              throws CLIException
          {
            String ninput = input.trim();
            if (ninput.length() == 0)
            {
              if (useAdmin)
              {
                return tmpAdminUID;
              }
              else
              {
                return tmpBindDN;
              }
            }
            else
            {
              return ninput;
            }
          }

        };

        try
        {
          app.println();
          if (useAdminOrBindDn)
          {
            String def = (adminUID != null) ? adminUID : bindDN;
            String v = app.readValidatedInput(
                INFO_LDAP_CONN_GLOBAL_ADMINISTRATOR_OR_BINDDN_PROMPT.get(def),
                callback);
            if (Utils.isDn(v))
            {
              bindDN = v;
              providedBindDN = v;
              adminUID = null;
              providedAdminUID = null;
            }
            else
            {
              bindDN = null;
              providedBindDN = null;
              adminUID = v;
              providedAdminUID = v;
            }
          }
          else if (useAdmin)
          {
            adminUID = app.readValidatedInput(
                INFO_LDAP_CONN_PROMPT_ADMINISTRATOR_UID.get(adminUID),
                callback);
            providedAdminUID = adminUID;
          }
          else
          {
            bindDN = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_BIND_DN
              .get(bindDN), callback);
            providedBindDN = bindDN;
          }
        }
        catch (CLIException e)
        {
          throw ArgumentExceptionFactory
              .unableToReadConnectionParameters(e);
        }
      }
      if (useAdminOrBindDn)
      {
        boolean addAdmin = providedAdminUID != null;
        boolean addBindDN = providedBindDN != null;
        if (!addAdmin && !addBindDN)
        {
          addAdmin = getAdministratorUID() != null;
          addBindDN = getBindDN() != null;
        }
        if (addAdmin)
        {
          copySecureArgsList.adminUidArg.clearValues();
          copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
          commandBuilder.addArgument(copySecureArgsList.adminUidArg);
        }
        else if (addBindDN)
        {
          copySecureArgsList.bindDnArg.clearValues();
          copySecureArgsList.bindDnArg.addValue(getBindDN());
          commandBuilder.addArgument(copySecureArgsList.bindDnArg);
        }
      }
      else if (useAdmin)
      {
        copySecureArgsList.adminUidArg.clearValues();
        copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
        commandBuilder.addArgument(copySecureArgsList.adminUidArg);
      }
      else
      {
        copySecureArgsList.bindDnArg.clearValues();
        copySecureArgsList.bindDnArg.addValue(getBindDN());
        commandBuilder.addArgument(copySecureArgsList.bindDnArg);
      }
    }
    else
    {
      bindDN = null;
      adminUID = null;
    }

    boolean addedPasswordFileArgument = false;
    bindPassword = secureArgsList.bindPasswordArg.getValue();
    if (keyManager == null)
    {
      if (secureArgsList.bindPasswordFileArg.isPresent())
      {
        // Read from file if it exists.
        bindPassword = secureArgsList.bindPasswordFileArg.getValue();

        if (bindPassword == null)
        {
          if (useAdmin)
          {
            throw ArgumentExceptionFactory.missingBindPassword(adminUID);
          }
          else
          {
            throw ArgumentExceptionFactory.missingBindPassword(bindDN);
          }
        }
        copySecureArgsList.bindPasswordFileArg.clearValues();
        copySecureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(
            secureArgsList.bindPasswordFileArg.getNameToValueMap());
        commandBuilder.addArgument(copySecureArgsList.bindPasswordFileArg);
        addedPasswordFileArgument = true;
      }
      else if (bindPassword == null || bindPassword.equals("-"))
      {
        // Read the password from the stdin.
        if (!app.isInteractive())
        {
          throw ArgumentExceptionFactory
              .unableToReadBindPasswordInteractively();
        }

        checkHeadingDisplayed();

        try
        {
          app.println();
          Message prompt;
          if (providedAdminUID != null)
          {
            prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedAdminUID);
          }
          else if (providedBindDN != null)
          {
            prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedBindDN);
          }
          else if (bindDN != null)
          {
            prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
          }
          else
          {
            prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(adminUID);
          }
          bindPassword = app.readPassword(prompt);
        }
        catch (Exception e)
        {
          throw ArgumentExceptionFactory
              .unableToReadConnectionParameters(e);
        }
      }
      copySecureArgsList.bindPasswordArg.clearValues();
      copySecureArgsList.bindPasswordArg.addValue(bindPassword);
      if (!addedPasswordFileArgument)
      {
        commandBuilder.addObfuscatedArgument(
          copySecureArgsList.bindPasswordArg);
      }
    }
    connectTimeout = secureArgsList.connectTimeoutArg.getIntValue();
  }

  /**
   * Get the trust manager.
   *
   * @return The trust manager based on CLI args on interactive prompt.
   * @throws ArgumentException If an error occurs when getting args values.
   */
  private ApplicationTrustManager getTrustManagerInternal()
  throws ArgumentException
  {
    // Remove these arguments since this method might be called several times.
    commandBuilder.removeArgument(copySecureArgsList.trustAllArg);
    commandBuilder.removeArgument(copySecureArgsList.trustStorePathArg);
    commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordArg);
    commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordFileArg);

    // If we have the trustALL flag, don't do anything
    // just return null
    if (secureArgsList.trustAllArg.isPresent())
    {
      commandBuilder.addArgument(copySecureArgsList.trustAllArg);
      return null;
    }

      // Check if some trust manager info are set
    boolean weDontKnowTheTrustMethod =
      !(  secureArgsList.trustAllArg.isPresent()
          ||
          secureArgsList.trustStorePathArg.isPresent()
          ||
          secureArgsList.trustStorePasswordArg.isPresent()
          ||
          secureArgsList.trustStorePasswordFileArg.isPresent()
        );
    boolean askForTrustStore = false;

    trustAll = secureArgsList.trustAllArg.isPresent();

    // Try to use the local instance trust store, to avoid certificate
    // validation when both the CLI and the server are in the same instance.
    if (weDontKnowTheTrustMethod) {
      if (addLocalTrustStore()) {
        weDontKnowTheTrustMethod = false;

      }
    }

    if (app.isInteractive() && weDontKnowTheTrustMethod)
    {
      checkHeadingDisplayed();

      app.println();
      MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
      builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_METHOD.get());

      TrustMethod defaultTrustMethod = TrustMethod.DISPLAY_CERTIFICATE;
      for (TrustMethod t : TrustMethod.values())
      {
        int i = builder.addNumberedOption(t.getMenuMessage(), MenuResult
            .success(t.getChoice()));
        if (t.equals(defaultTrustMethod))
        {
          builder.setDefault(
              INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
                  .get(new Integer(i)), MenuResult.success(t.getChoice()));
        }
      }

      Menu<Integer> menu = builder.toMenu();
      trustStoreInMemory = false;
      try
      {
        MenuResult<Integer> result = menu.run();
        if (result.isSuccess())
        {
          if (result.getValue().equals(TrustMethod.TRUSTALL.getChoice()))
          {
            commandBuilder.addArgument(copySecureArgsList.trustAllArg);
            trustAll = true;
            // If we have the trustALL flag, don't do anything
            // just return null
            return null;
          }
          else if (result.getValue().equals(
              TrustMethod.TRUSTSTORE.getChoice()))
          {
            // We have to ask for trust store info
            askForTrustStore = true;
          }
          else if (result.getValue().equals(
              TrustMethod.DISPLAY_CERTIFICATE.getChoice()))
          {
            // The certificate will be displayed to the user
            askForTrustStore = false;
            trustStoreInMemory = true;

            // There is no direct equivalent for this option, so propose the
            // trust all option as command-line argument.
            commandBuilder.addArgument(copySecureArgsList.trustAllArg);
          }
          else
          {
            // Should never happen.
            throw new RuntimeException();
          }
        }
        else
        {
          // Should never happen.
          throw new RuntimeException();
        }
      }
      catch (CLIException e)
      {
        throw new RuntimeException(e);

      }
    }

    // If we do not trust all server certificates, we have to get info
    // about trust store. First get the trust store path.
    truststorePath = secureArgsList.trustStorePathArg.getValue();

    if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent()
        && askForTrustStore)
    {
      checkHeadingDisplayed();

      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            app.println();
            app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
          File f = new File(ninput);
          if (f.exists() && f.canRead() && !f.isDirectory())
          {
            return ninput;
          }
          else
          {
            app.println();
            app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
        }
      };

      try
      {
        app.println();
        truststorePath = app.readValidatedInput(
            INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }

    if (truststorePath != null)
    {
      copySecureArgsList.trustStorePathArg.clearValues();
      copySecureArgsList.trustStorePathArg.addValue(truststorePath);
      commandBuilder.addArgument(copySecureArgsList.trustStorePathArg);
    }

    // Then the truststore password.
    //  As the most common case is to have no password for truststore,
    // we don't ask it in the interactive mode.
    truststorePassword = secureArgsList.trustStorePasswordArg
        .getValue();

    if (secureArgsList.trustStorePasswordFileArg.isPresent())
    {
      // Read from file if it exists.
      truststorePassword = secureArgsList.trustStorePasswordFileArg
          .getValue();
    }
    if ((truststorePassword !=  null) && (truststorePassword.equals("-")))
    {
      // Read the password from the stdin.
      if (!app.isInteractive())
      {
        truststorePassword = null;
      }
      else
      {
        checkHeadingDisplayed();

        try
        {
          app.println();
          Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD
              .get(truststorePath);
          truststorePassword = app.readPassword(prompt);
        }
        catch (Exception e)
        {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
    }

    // We've got all the information to get the truststore manager
    try
    {
      truststore = KeyStore.getInstance(KeyStore.getDefaultType());
      if (truststorePath != null)
      {
        FileInputStream fos = new FileInputStream(truststorePath);
        if (truststorePassword != null)
        {
          truststore.load(fos, truststorePassword.toCharArray());
        }
        else
        {
          truststore.load(fos, null);
        }
        fos.close();
      }
      else
      {
        truststore.load(null, null);
      }

      if (secureArgsList.trustStorePasswordFileArg.isPresent() &&
          (truststorePath != null))
      {
        copySecureArgsList.trustStorePasswordFileArg.clearValues();
        copySecureArgsList.trustStorePasswordFileArg.getNameToValueMap().putAll(
            secureArgsList.trustStorePasswordFileArg.getNameToValueMap());
        commandBuilder.addArgument(
            copySecureArgsList.trustStorePasswordFileArg);
      }
      else if ((truststorePassword != null) && (truststorePath != null))
      {
        // Only add the trust store password if there is one AND if the user
        // specified a trust store path.
        copySecureArgsList.trustStorePasswordArg.clearValues();
        copySecureArgsList.trustStorePasswordArg.addValue(truststorePassword);
        commandBuilder.addObfuscatedArgument(
            copySecureArgsList.trustStorePasswordArg);
      }

      return new ApplicationTrustManager(truststore);
    }
    catch (Exception e)
    {
      throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
    }
  }

  /**
   * Get the key manager.
   *
   * @return The key manager based on CLI args on interactive prompt.
   * @throws ArgumentException If an error occurs when getting args values.
   */
  private KeyManager getKeyManagerInternal()
  throws ArgumentException
  {
//  Remove these arguments since this method might be called several times.
    commandBuilder.removeArgument(copySecureArgsList.certNicknameArg);
    commandBuilder.removeArgument(copySecureArgsList.keyStorePathArg);
    commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordArg);
    commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordFileArg);

    // Do we need client side authentication ?
    // If one of the client side authentication args is set, we assume
    // that we
    // need client side authentication.
    boolean weDontKnowIfWeNeedKeystore = !(secureArgsList.keyStorePathArg
        .isPresent()
        || secureArgsList.keyStorePasswordArg.isPresent()
        || secureArgsList.keyStorePasswordFileArg.isPresent()
        || secureArgsList.certNicknameArg
        .isPresent());

    // We don't have specific key manager parameter.
    // We assume that no client side authentication is required
    // Client side authentication is not the common use case. As a
    // consequence, interactive mode doesn't add an extra question
    // which will be in most cases useless.
    if (weDontKnowIfWeNeedKeystore)
    {
      return null;
    }

    // Get info about keystore. First get the keystore path.
    keystorePath = secureArgsList.keyStorePathArg.getValue();
    if (app.isInteractive() && !secureArgsList.keyStorePathArg.isPresent())
    {
      checkHeadingDisplayed();

      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return ninput;
          }
          File f = new File(ninput);
          if (f.exists() && f.canRead() && !f.isDirectory())
          {
            return ninput;
          }
          else
          {
            app.println();
            app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
        }
      };

      try
      {
        app.println();
        keystorePath = app.readValidatedInput(
            INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }

    if (keystorePath != null)
    {
      copySecureArgsList.keyStorePathArg.clearValues();
      copySecureArgsList.keyStorePathArg.addValue(keystorePath);
      commandBuilder.addArgument(copySecureArgsList.keyStorePathArg);
    }


    // Then the keystore password.
    keystorePassword = secureArgsList.keyStorePasswordArg.getValue();

    if (secureArgsList.keyStorePasswordFileArg.isPresent())
    {
      // Read from file if it exists.
      keystorePassword = secureArgsList.keyStorePasswordFileArg.getValue();

      if (keystorePassword == null)
      {
        throw ArgumentExceptionFactory.missingBindPassword(keystorePassword);
      }
    }
    else if (keystorePassword == null || keystorePassword.equals("-"))
    {
      // Read the password from the stdin.
      if (!app.isInteractive())
      {
        throw ArgumentExceptionFactory
            .unableToReadBindPasswordInteractively();
      }

      checkHeadingDisplayed();

      try
      {
        app.println();
        Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
            .get(keystorePath);
        keystorePassword = app.readPassword(prompt);
      }
      catch (Exception e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }

    // finally the certificate name, if needed.
    KeyStore keystore = null;
    Enumeration<String> aliasesEnum = null;
    try
    {
      FileInputStream fos = new FileInputStream(keystorePath);
      keystore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(fos, keystorePassword.toCharArray());
      fos.close();
      aliasesEnum = keystore.aliases();
    }
    catch (Exception e)
    {
      throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
    }

    certifNickname = secureArgsList.certNicknameArg.getValue();
    if (app.isInteractive() && !secureArgsList.certNicknameArg.isPresent()
        && aliasesEnum.hasMoreElements())
    {
      checkHeadingDisplayed();

      try
      {
        MenuBuilder<String> builder = new MenuBuilder<String>(app);
        builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIASES
            .get());
        int certificateNumber = 0;
        for (; aliasesEnum.hasMoreElements();)
        {
          String alias = aliasesEnum.nextElement();
          if (keystore.isKeyEntry(alias))
          {
            X509Certificate certif = (X509Certificate) keystore
                .getCertificate(alias);
            certificateNumber++;
            builder.addNumberedOption(
                INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIAS.get(alias,
                    certif.getSubjectDN().getName()), MenuResult
                    .success(alias));
          }
        }

        if (certificateNumber > 1)
        {
          app.println();
          Menu<String> menu = builder.toMenu();
          MenuResult<String> result = menu.run();
          if (result.isSuccess())
          {
            certifNickname = result.getValue();
          }
          else
          {
            // Should never happen.
            throw new RuntimeException();
          }
        }
        else
        {
          certifNickname = null;
        }
      }
      catch (KeyStoreException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }

    // We'we got all the information to get the keys manager
    ApplicationKeyManager akm = new ApplicationKeyManager(keystore,
        keystorePassword.toCharArray());


    if (secureArgsList.keyStorePasswordFileArg.isPresent())
    {
      copySecureArgsList.keyStorePasswordFileArg.clearValues();
      copySecureArgsList.keyStorePasswordFileArg.getNameToValueMap().putAll(
          secureArgsList.keyStorePasswordFileArg.getNameToValueMap());
      commandBuilder.addArgument(
          copySecureArgsList.keyStorePasswordFileArg);
    }
    else if (keystorePassword != null)
    {
      copySecureArgsList.keyStorePasswordArg.clearValues();
      copySecureArgsList.keyStorePasswordArg.addValue(keystorePassword);
      commandBuilder.addObfuscatedArgument(
          copySecureArgsList.keyStorePasswordArg);
    }

    if (certifNickname != null)
    {
      copySecureArgsList.certNicknameArg.clearValues();
      copySecureArgsList.certNicknameArg.addValue(certifNickname);
    }

    if (certifNickname != null)
    {
      return new SelectableCertificateKeyManager(akm, certifNickname);
    }
    else
    {
      return akm;
    }
  }

  /**
   * Indicates whether or not a connection should use SSL based on
   * this interaction.
   *
   * @return boolean where true means use SSL
   */
  public boolean useSSL() {
    return useSSL;
  }

  /**
   * Indicates whether or not a connection should use StartTLS based on
   * this interaction.
   *
   * @return boolean where true means use StartTLS
   */
  public boolean useStartTLS() {
    return useStartTLS;
  }

  /**
   * Gets the host name that should be used for connections based on
   * this interaction.
   *
   * @return host name for connections
   */
  public String getHostName() {
    return hostName;
  }

  /**
   * Gets the port number name that should be used for connections based on
   * this interaction.
   *
   * @return port number for connections
   */
  public int getPortNumber() {
    return portNumber;
  }

  /**
   * Sets the port number name that should be used for connections based on
   * this interaction.
   *
   * @param portNumber port number for connections
   */
  public void setPortNumber(int portNumber) {
    this.portNumber = portNumber;
  }

  /**
   * Gets the bind DN name that should be used for connections based on
   * this interaction.
   *
   * @return bind DN for connections
   */
  public String getBindDN() {
    String dn;
    if (useAdminOrBindDn)
    {
      if (providedBindDN != null)
      {
        dn = providedBindDN;
      }
      else if (providedAdminUID != null)
      {
        dn = ADSContext.getAdministratorDN(providedAdminUID);
      }
      else if (this.bindDN != null)
      {
        dn = this.bindDN;
      }
      else if (this.adminUID != null)
      {
        dn = ADSContext.getAdministratorDN(this.adminUID);
      }
      else
      {
        dn = null;
      }
    }
    else if (secureArgsList.useAdminUID())
    {
      dn = ADSContext.getAdministratorDN(this.adminUID);
    }
    else
    {
      dn = this.bindDN;
    }
    return dn;
  }

  /**
   * Gets the administrator UID name that should be used for connections based
   * on this interaction.
   *
   * @return administrator UID for connections
   */
  public String getAdministratorUID() {
    return this.adminUID;
  }

  /**
   * Gets the bind password that should be used for connections based on
   * this interaction.
   *
   * @return bind password for connections
   */
  public String getBindPassword() {
    return this.bindPassword;
  }

  /**
   * Gets the trust manager that should be used for connections based on
   * this interaction.
   *
   * @return trust manager for connections
   */
  public ApplicationTrustManager getTrustManager() {
    return this.trustManager;
  }

  /**
   * Gets the key store that should be used for connections based on
   * this interaction.
   *
   * @return key store for connections
   */
  public KeyStore getKeyStore() {
    return this.truststore;
  }

  /**
   * Gets the key manager that should be used for connections based on
   * this interaction.
   *
   * @return key manager for connections
   */
  public KeyManager getKeyManager() {
    return this.keyManager;
  }

  /**
   * Indicate if the trust store is in memory.
   *
   * @return true if the trust store is in memory.
   */
  public boolean isTrustStoreInMemory() {
    return this.trustStoreInMemory;
  }

  /**
   * Indicate if all certificates must be accepted.
   *
   * @return true all certificates must be accepted.
   */
  public boolean isTrustAll() {
    return this.trustAll;
  }

  /**
   * Returns the timeout to be used to connect with the server.
   * @return the timeout to be used to connect with the server.
   */
  public int getConnectTimeout()
  {
    return connectTimeout;
  }

  /**
   * Indicate if the certificate chain can be trusted.
   *
   * @param chain The certificate chain to validate
   * @return true if the server certificate is trusted.
   */
  public boolean checkServerCertificate(X509Certificate[] chain)
  {
    return checkServerCertificate(chain, null, null);
  }

  /**
   * Indicate if the certificate chain can be trusted.
   *
   * @param chain The certificate chain to validate
   * @param authType the authentication type.
   * @param host the host we tried to connect and that presented the
   * certificate.
   * @return true if the server certificate is trusted.
   */
  public boolean checkServerCertificate(X509Certificate[] chain,
      String authType, String host)
    {
    if (trustManager == null)
    {
      try
      {
        initializeTrustManager();
      }
      catch (ArgumentException ae)
      {
        // Should not occur
        throw new RuntimeException(ae);
      }
    }
    app.println();
    app.println(INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE.get());
    app.println();
    for (int i = 0; i < chain.length; i++)
    {
      // Certificate DN
      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN.get(
          chain[i].getSubjectDN().toString()));

      // certificate validity
      app.println(
          INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY.get(
              chain[i].getNotBefore().toString(),
              chain[i].getNotAfter().toString()));

      // certificate Issuer
      app.println(
          INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER.get(
              chain[i].getIssuerDN().toString()));

      if (i+1 <chain.length)
      {
        app.println();
        app.println();
      }
    }
    MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
    builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION.get());

    TrustOption defaultTrustMethod = TrustOption.SESSION ;
    for (TrustOption t : TrustOption.values())
    {
      int i = builder.addNumberedOption(t.getMenuMessage(), MenuResult
          .success(t.getChoice()));
      if (t.equals(defaultTrustMethod))
      {
        builder.setDefault(
            INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
                .get(new Integer(i)), MenuResult.success(t.getChoice()));
      }
    }

    app.println();
    app.println();

    Menu<Integer> menu = builder.toMenu();
    while (true)
    {
      try
      {
        MenuResult<Integer> result = menu.run();
        if (result.isSuccess())
        {
          if (result.getValue().equals(TrustOption.UNTRUSTED.getChoice()))
          {
            return false;
          }

          if ((result.getValue().equals(TrustOption.CERTIFICATE_DETAILS
              .getChoice())))
          {
            for (int i = 0; i < chain.length; i++)
            {
              app.println();
              app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE
                  .get(chain[i].toString()));
            }
            continue;
          }

          // We should add it in the memory truststore
          for (int i = 0; i < chain.length; i++)
          {
            String alias = chain[i].getSubjectDN().getName();
            try
            {
              truststore.setCertificateEntry(alias, chain[i]);
            }
            catch (KeyStoreException e1)
            {
              // What else should we do?
              return false;
            }
          }

          // Update the trust manager
          if (trustManager == null)
          {
            trustManager = new ApplicationTrustManager(truststore);
          }
          if ((authType != null) && (host != null))
          {
            // Update the trust manager with the new certificate
            trustManager.acceptCertificate(chain, authType, host);
          }
          else
          {
            // Do a full reset of the contents of the keystore.
            trustManager = new ApplicationTrustManager(truststore);
          }
          if (result.getValue().equals(TrustOption.PERMAMENT.getChoice()))
          {
            ValidationCallback<String> callback =
              new ValidationCallback<String>()
            {
              public String validate(ConsoleApplication app, String input)
                  throws CLIException
              {
                String ninput = input.trim();
                if (ninput.length() == 0)
                {
                  app.println();
                  app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                      .get());
                  app.println();
                  return null;
                }
                File f = new File(ninput);
                if (!f.isDirectory())
                {
                  return ninput;
                }
                else
                {
                  app.println();
                  app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                      .get());
                  app.println();
                  return null;
                }
              }
            };

            String truststorePath;
            try
            {
              app.println();
              truststorePath = app.readValidatedInput(
                  INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(),
                  callback);
            }
            catch (CLIException e)
            {
              return true;
            }

            // Read the password from the stdin.
            String truststorePassword;
            try
            {
              app.println();
              Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
                  .get(truststorePath);
              truststorePassword = app.readPassword(prompt);
            }
            catch (Exception e)
            {
              return true;
            }
            try
            {
              KeyStore ts = KeyStore.getInstance("JKS");
              FileInputStream fis;
              try
              {
                fis = new FileInputStream(truststorePath);
              }
              catch (FileNotFoundException e)
              {
                fis = null;
              }
              ts.load(fis, truststorePassword.toCharArray());
              if (fis != null)
              {
                fis.close();
              }
              for (int i = 0; i < chain.length; i++)
              {
                String alias = chain[i].getSubjectDN().getName();
                ts.setCertificateEntry(alias, chain[i]);
              }
              FileOutputStream fos = new FileOutputStream(truststorePath);
              ts.store(fos, truststorePassword.toCharArray());
              if (fos != null)
              {
                fos.close();
              }
            }
            catch (Exception e)
            {
              return true;
            }
          }
          return true;
        }
        else
        {
          // Should never happen.
          throw new RuntimeException();
        }
      }
      catch (CLIException cliE)
      {
        throw new RuntimeException(cliE);
      }
    }
  }

 /**
  * Populates a set of LDAP options with state from this interaction.
  *
  * @param  options existing set of options; may be null in which case this
  *         method will create a new set of <code>LDAPConnectionOptions</code>
  *         to be returned
  * @return used during this interaction
  * @throws SSLConnectionException if this interaction has specified the use
  *         of SSL and there is a problem initializing the SSL connection
  *         factory
  */
 public LDAPConnectionOptions populateLDAPOptions(
         LDAPConnectionOptions options)
         throws SSLConnectionException
 {
   if (options == null) {
     options = new LDAPConnectionOptions();
   }
   if (this.useSSL) {
     options.setUseSSL(true);
     SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
     sslConnectionFactory.init(getTrustManager() == null, keystorePath,
                               keystorePassword, certifNickname,
                               truststorePath, truststorePassword);
     options.setSSLConnectionFactory(sslConnectionFactory);
   } else {
     options.setUseSSL(false);
   }
   options.setStartTLS(this.useStartTLS);
   return options;
 }

 /**
  * Prompts the user to accept the certificate.
  * @param t the throwable that was generated because the certificate was
  * not trusted.
  * @param usedTrustManager the trustManager used when trying to establish the
  * connection.
  * @param usedUrl the LDAP URL used to connect to the server.
  * @param displayErrorMessage whether to display an error message before
  * asking to accept the certificate or not.
  * @param logger the Logger used to log messages.
  * @return <CODE>true</CODE> if the user accepted the certificate and
  * <CODE>false</CODE> otherwise.
  */
 public boolean promptForCertificateConfirmation(Throwable t,
     ApplicationTrustManager usedTrustManager, String usedUrl,
     boolean displayErrorMessage, Logger logger)
 {
   boolean returnValue = false;
   ApplicationTrustManager.Cause cause;
   if (usedTrustManager != null)
   {
     cause = usedTrustManager.getLastRefusedCause();
   }
   else
   {
     cause = null;
   }
   if (logger != null)
   {
     logger.log(Level.INFO, "Certificate exception cause: "+cause);
   }
   UserDataCertificateException.Type excType = null;
   if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
   {
     excType = UserDataCertificateException.Type.NOT_TRUSTED;
   }
   else if (cause ==
     ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
   {
     excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH;
   }
   else
   {
     Message msg = Utils.getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(),
         t);
     app.println(msg);
   }

   if (excType != null)
   {
     String h;
     int p;
     try
     {
       URI uri = new URI(usedUrl);
       h = uri.getHost();
       p = uri.getPort();
     }
     catch (Throwable t1)
     {
       if (logger != null)
       {
         logger.log(Level.WARNING, "Error parsing ldap url of ldap url.", t1);
       }
       h = INFO_NOT_AVAILABLE_LABEL.get().toString();
       p = -1;
     }



     UserDataCertificateException udce =
       new UserDataCertificateException(Step.REPLICATION_OPTIONS,
           INFO_CERTIFICATE_EXCEPTION.get(h, String.valueOf(p)), t, h, p,
               usedTrustManager.getLastRefusedChain(),
               usedTrustManager.getLastRefusedAuthType(), excType);

     Message msg;
     if (udce.getType() == UserDataCertificateException.Type.NOT_TRUSTED)
     {
       msg = INFO_CERTIFICATE_NOT_TRUSTED_TEXT_CLI.get(
           udce.getHost(), String.valueOf(udce.getPort()));
     }
     else
     {
       msg = INFO_CERTIFICATE_NAME_MISMATCH_TEXT_CLI.get(
           udce.getHost(), String.valueOf(udce.getPort()),
           udce.getHost(),
           udce.getHost(), String.valueOf(udce.getPort()));
     }
     if (displayErrorMessage)
     {
       app.println(msg);
     }
     X509Certificate[] chain = udce.getChain();
     String authType = udce.getAuthType();
     String host = udce.getHost();
     if (logger != null)
     {
       if (chain == null)
       {
         logger.log(Level.WARNING,
         "The chain is null for the UserDataCertificateException");
       }
       if (authType == null)
       {
         logger.log(Level.WARNING,
         "The auth type is null for the UserDataCertificateException");
       }
       if (host == null)
       {
         logger.log(Level.WARNING,
         "The host is null for the UserDataCertificateException");
       }
     }
     if (chain != null)
     {
       returnValue = checkServerCertificate(chain, authType, host);
     }
   }
   return returnValue;
 }

 /**
  * Sets the heading that is displayed in interactive mode.
  * @param heading the heading that is displayed in interactive mode.
  */
 public void setHeadingMessage(Message heading)
 {
   this.heading = heading;
 }

 /**
  * Returns the command builder with the equivalent arguments on the
  * non-interactive mode.
  * @return the command builder with the equivalent arguments on the
  * non-interactive mode.
  */
 public CommandBuilder getCommandBuilder()
 {
   return commandBuilder;
 }

 /**
  * Displays the heading if it was not displayed before.
  *
  */
 private void checkHeadingDisplayed()
 {
   if (!isHeadingDisplayed)
   {
     app.println();
     app.println();
     app.println(heading);
     isHeadingDisplayed = true;
   }
 }

 /**
  * Tells whether during interaction we can ask for both the DN or the admin
  * UID.
  * @return <CODE>true</CODE> if during interaction we can ask for both the DN
  * and the admin UID and <CODE>false</CODE> otherwise.
  */
 public boolean isUseAdminOrBindDn()
 {
   return useAdminOrBindDn;
 }

 /**
  * Tells whether we can ask during interaction for both the DN and the admin
  * UID or not.
  * @param useAdminOrBindDn whether we can ask for both the DN and the admin UID
  * during interaction or not.
  */
 public void setUseAdminOrBindDn(boolean useAdminOrBindDn)
 {
   this.useAdminOrBindDn = useAdminOrBindDn;
 }

 /**
  * Tells whether we propose LDAP as protocol even if the user provided security
  * parameters.  This is required in command-lines that access multiple servers
  * (like dsreplication).
  * @param displayLdapIfSecureParameters whether propose LDAP as protocol even
  * if the user provided security parameters or not.
  */
 public void setDisplayLdapIfSecureParameters(
     boolean displayLdapIfSecureParameters)
 {
   this.displayLdapIfSecureParameters = displayLdapIfSecureParameters;
 }

 /**
  * Resets the heading displayed flag, so that next time we call run the heading
  * is displayed.
  */
 public void resetHeadingDisplayed()
 {
   isHeadingDisplayed = false;
 }

 /**
  * Resets the trust manager, so that next time we call the run() method
  * the trust manager takes into account the local trust store.
  */
 public void resetTrustManager()
 {
   trustManager = null;
 }

 /**
  * Forces the initialization of the trust manager with the arguments provided
  * by the user.
  * @throws ArgumentException if there is an error with the arguments provided
  * by the user.
  */
 public void initializeTrustManagerIfRequired() throws ArgumentException
 {
   if (!trustManagerInitialized)
   {
     initializeTrustManager();
   }
 }

 /**
  * Initializes the global arguments in the parser with the provided values.
  * This is useful when we want to call LDAPConnectionConsoleInteraction.run()
  * with some default values.
  * @param hostName the host name.
  * @param port the port to connect to the server.
  * @param adminUid the administrator UID.
  * @param bindDn the bind DN to bind to the server.
  * @param bindPwd the password to bind.
  * @param pwdFile the Map containing the file and the password to bind.
  */
 public void initializeGlobalArguments(String hostName, int port,
     String adminUid, String bindDn,
     String bindPwd, LinkedHashMap<String, String> pwdFile)
 {
   resetConnectionArguments();
   if (hostName != null)
   {
     secureArgsList.hostNameArg.addValue(hostName);
     secureArgsList.hostNameArg.setPresent(true);
   }
   // resetConnectionArguments does not clear the values for the port
   secureArgsList.portArg.clearValues();
   if (port != -1)
   {
     secureArgsList.portArg.addValue(String.valueOf(port));
     secureArgsList.portArg.setPresent(true);
   }
   else
   {
     // This is done to be able to call IntegerArgument.getIntValue()
     secureArgsList.portArg.addValue(secureArgsList.portArg.getDefaultValue());
   }
   secureArgsList.useSSLArg.setPresent(useSSL);
   secureArgsList.useStartTLSArg.setPresent(useStartTLS);
   if (adminUid != null)
   {
     secureArgsList.adminUidArg.addValue(adminUid);
     secureArgsList.adminUidArg.setPresent(true);
   }
   if (bindDn != null)
   {
     secureArgsList.bindDnArg.addValue(bindDn);
     secureArgsList.bindDnArg.setPresent(true);
   }
   if (pwdFile != null)
   {
     secureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(pwdFile);
     for (String value : pwdFile.keySet())
     {
       secureArgsList.bindPasswordFileArg.addValue(value);
     }
     secureArgsList.bindPasswordFileArg.setPresent(true);
   }
   else if (bindPwd != null)
   {
     secureArgsList.bindPasswordArg.addValue(bindPwd);
     secureArgsList.bindPasswordArg.setPresent(true);
   }
 }

 /**
  * Resets the connection parameters for the LDAPConsoleInteraction  object.
  * The reset does not apply to the certificate parameters.  This is called
  * in order the LDAPConnectionConsoleInteraction object to ask for all this
  * connection parameters next time we call
  * LDAPConnectionConsoleInteraction.run().
  */
 public void resetConnectionArguments()
 {
   secureArgsList.hostNameArg.clearValues();
   secureArgsList.hostNameArg.setPresent(false);
   secureArgsList.portArg.clearValues();
   secureArgsList.portArg.setPresent(false);
   //  This is done to be able to call IntegerArgument.getIntValue()
   secureArgsList.portArg.addValue(secureArgsList.portArg.getDefaultValue());
   secureArgsList.bindDnArg.clearValues();
   secureArgsList.bindDnArg.setPresent(false);
   secureArgsList.bindPasswordArg.clearValues();
   secureArgsList.bindPasswordArg.setPresent(false);
   secureArgsList.bindPasswordFileArg.clearValues();
   secureArgsList.bindPasswordFileArg.getNameToValueMap().clear();
   secureArgsList.bindPasswordFileArg.setPresent(false);
   secureArgsList.adminUidArg.clearValues();
   secureArgsList.adminUidArg.setPresent(false);
 }

 private void initializeTrustManager() throws ArgumentException
 {
   // Get trust store info
   trustManager = getTrustManagerInternal();

   // Check if we need client side authentication
   keyManager = getKeyManagerInternal();

   trustManagerInitialized = true;
 }
 /**
  * Returns the explicitly provided Admin UID from the user (interactively
  * or through the argument).
  * @return the explicitly provided Admin UID from the user (interactively
  * or through the argument).
  */
 public String getProvidedAdminUID()
 {
   return providedAdminUID;
 }

 /**
  * Returns the explicitly provided bind DN from the user (interactively
  * or through the argument).
  * @return the explicitly provided bind DN from the user (interactively
  * or through the argument).
  */
 public String getProvidedBindDN()
 {
   return providedBindDN;
 }

 /**
  * Add the TrustStore of the administration connector of the local instance.
  *
  *  @return true if the local trust store has been added.
  */
  private boolean addLocalTrustStore()
  {
    try {
      // If remote host, return
      if (!InetAddress.getLocalHost().getHostName().equals(hostName)) {
        return false;
      }
      // check if we are in a local instance. Already checked the host,
      // now check the port
      if (secureArgsList.getAdminPortFromConfig() != portNumber) {
        return false;
      }

      String truststoreFileAbsolute =
        secureArgsList.getTruststoreFileFromConfig();
      if (truststoreFileAbsolute != null)
      {
        secureArgsList.trustStorePathArg.addValue(truststoreFileAbsolute);
        return true;
      }
      else
      {
        return false;
      }
    } catch (Exception ex) {
      // do nothing
      return false;
    }
  }
}
