From 1932d41262099a16f43749a90677366818b3664c Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Tue, 01 Mar 2016 09:44:57 +0000
Subject: [PATCH] OPENDJ-2701 Code cleanup

---
 opendj-server-legacy/src/main/java/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java | 2227 ++++++++++++++++++++++++++--------------------------------
 1 files changed, 1,001 insertions(+), 1,226 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java b/opendj-server-legacy/src/main/java/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
index d567cf1..c4569cb 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
@@ -16,25 +16,31 @@
  */
 package org.opends.server.util.cli;
 
+import static com.forgerock.opendj.cli.Utils.portValidationCallback;
 import static com.forgerock.opendj.cli.Utils.isDN;
 import static com.forgerock.opendj.cli.Utils.getAdministratorDN;
 import static com.forgerock.opendj.cli.Utils.getThrowableMsg;
 import static org.opends.messages.ToolMessages.*;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.net.InetAddress;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.UnknownHostException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.cert.X509Certificate;
 import java.util.Enumeration;
 import java.util.LinkedHashMap;
+import java.util.Map;
 
 import javax.net.ssl.KeyManager;
 
+import com.forgerock.opendj.cli.Argument;
+import com.forgerock.opendj.cli.FileBasedArgument;
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.opends.admin.ads.util.ApplicationKeyManager;
@@ -65,6 +71,13 @@
 public class LDAPConnectionConsoleInteraction
 {
 
+  private static final Protocol DEFAULT_PROMPT_PROTOCOL = Protocol.SSL;
+  private static final TrustMethod DEFAULT_PROMPT_TRUST_METHOD = TrustMethod.DISPLAY_CERTIFICATE;
+  private static final TrustOption DEFAULT_PROMPT_TRUST_OPTION = TrustOption.SESSION;
+
+  private static final boolean ALLOW_EMPTY_PATH = true;
+  private static final boolean FILE_MUST_EXISTS = true;
+
   /**
    * Information from the latest console interaction.
    * TODO: should it extend MonoServerReplicationUserData or a subclass?
@@ -97,71 +110,61 @@
     private String truststorePassword;
 
     private KeyManager keyManager;
-    private String keystorePath;
+    private String keyStorePath;
     private String keystorePassword;
     private String certifNickname;
 
     private State(SecureConnectionCliArgs secureArgs)
     {
-      useSSL = secureArgs.useSSL();
-      useStartTLS = secureArgs.useStartTLS();
-      trustAll = secureArgs.trustAllArg.isPresent();
+      setSsl(secureArgs);
+      trustAll = secureArgs.getTrustAllArg().isPresent();
     }
 
-    /**
-     * @return
-     */
     protected LocalizableMessage getPrompt()
     {
-      LocalizableMessage prompt;
       if (providedAdminUID != null)
       {
-        prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedAdminUID);
+        return INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedAdminUID);
       }
       else if (providedBindDN != null)
       {
-        prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedBindDN);
+        return INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedBindDN);
       }
       else if (bindDN != null)
       {
-        prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
+        return INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
       }
-      else
-      {
-        prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(adminUID);
-      }
-      return prompt;
+
+      return INFO_LDAPAUTH_PASSWORD_PROMPT.get(adminUID);
     }
 
-    /**
-     * @return
-     */
     protected String getAdminOrBindDN()
     {
-      String dn;
       if (providedBindDN != null)
       {
-        dn = providedBindDN;
+        return providedBindDN;
       }
       else if (providedAdminUID != null)
       {
-        dn = getAdministratorDN(providedAdminUID);
+        return getAdministratorDN(providedAdminUID);
       }
       else if (bindDN != null)
       {
-        dn = bindDN;
+        return bindDN;
       }
       else if (adminUID != null)
       {
-        dn = getAdministratorDN(adminUID);
+        return getAdministratorDN(adminUID);
       }
-      else
-      {
-        dn = null;
-      }
-      return dn;
+
+      return null;
     }
 
+    private void setSsl(final SecureConnectionCliArgs secureArgs)
+    {
+      this.useSSL = secureArgs.alwaysSSL() || secureArgs.getUseSSLArg().isPresent();
+      this.useStartTLS = secureArgs.getUseStartTLSArg().isPresent();
+    }
   }
 
   /** The console application. */
@@ -170,7 +173,7 @@
   private State state;
 
   /** The SecureConnectionCliArgsList object. */
-  private SecureConnectionCliArgs secureArgsList;
+  private final SecureConnectionCliArgs secureArgsList;
 
   /** The command builder that we can return with the connection information. */
   private CommandBuilder commandBuilder;
@@ -192,147 +195,87 @@
   private boolean useAdminOrBindDn;
 
   /** Enumeration description protocols for interactive CLI choices. */
-  private enum Protocols
+  private enum Protocol
   {
-    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());
+    LDAP(INFO_LDAP_CONN_PROMPT_SECURITY_LDAP.get()),
+    SSL(INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL.get()),
+    START_TLS(INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TLS.get());
 
-    private Integer choice;
+    private final LocalizableMessage message;
 
-    private LocalizableMessage msg;
-
-    /**
-     * Private constructor.
-     *
-     * @param i
-     *          the menu return value.
-     * @param msg
-     *          the message message.
-     */
-    private Protocols(int i, LocalizableMessage msg)
+    Protocol(final LocalizableMessage message)
     {
-      choice = i;
-      this.msg = msg;
+      this.message = message;
     }
 
-    /**
-     * Returns the choice number.
-     *
-     * @return the attribute name.
-     */
-    public Integer getChoice()
+    private int getChoice()
     {
-      return choice;
-    }
-
-    /**
-     * Return the menu message.
-     *
-     * @return the menu message.
-     */
-    public LocalizableMessage getMenuMessage()
-    {
-      return msg;
+      return ordinal() + 1;
     }
   }
 
-  /**
-   * Enumeration description protocols for interactive CLI choices.
-   */
+  /** Enumeration description protocols for interactive CLI choices. */
   private enum TrustMethod
   {
-    TRUSTALL(1, INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get()),
+    TRUSTALL(INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get()),
+    TRUSTSTORE(INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE.get()),
+    DISPLAY_CERTIFICATE(INFO_LDAP_CONN_PROMPT_SECURITY_MANUAL_CHECK.get());
 
-    TRUSTSTORE(2, INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE.get()),
+    private LocalizableMessage message;
 
-    DISPLAY_CERTIFICATE(3, INFO_LDAP_CONN_PROMPT_SECURITY_MANUAL_CHECK.get());
-
-    private Integer choice;
-
-    private LocalizableMessage msg;
-
-    /**
-     * Private constructor.
-     *
-     * @param i
-     *          the menu return value.
-     * @param msg
-     *          the message message.
-     */
-    private TrustMethod(int i, LocalizableMessage msg)
+    TrustMethod(final LocalizableMessage message)
     {
-      choice = Integer.valueOf(i);
-      this.msg = msg;
+      this.message = message;
     }
 
-    /**
-     * Returns the choice number.
-     *
-     * @return the attribute name.
-     */
-    public Integer getChoice()
+    private int getChoice()
     {
-      return choice;
+      return ordinal() + 1;
     }
 
-    /**
-     * Return the menu message.
-     *
-     * @return the menu message.
-     */
-    public LocalizableMessage getMenuMessage()
+    private static TrustMethod getTrustMethodForIndex(final int value)
     {
-      return msg;
+      for (final TrustMethod trustMethod : TrustMethod.values())
+      {
+        if (trustMethod.getChoice() == value)
+        {
+          return trustMethod;
+        }
+      }
+      return null;
     }
   }
 
-  /**
-   * Enumeration description server certificate trust option.
-   */
+  /** 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());
+    UNTRUSTED(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO.get()),
+    SESSION(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION.get()),
+    PERMAMENT(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS.get()),
+    CERTIFICATE_DETAILS(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS.get());
 
-    private Integer choice;
+    private LocalizableMessage message;
 
-    private LocalizableMessage msg;
-
-    /**
-     * Private constructor.
-     *
-     * @param i
-     *          the menu return value.
-     * @param msg
-     *          the message message.
-     */
-    private TrustOption(int i, LocalizableMessage msg)
+    TrustOption(final LocalizableMessage message)
     {
-      choice = Integer.valueOf(i);
-      this.msg = msg;
+      this.message = message;
     }
 
-    /**
-     * Returns the choice number.
-     *
-     * @return the attribute name.
-     */
-    public Integer getChoice()
+    private int getChoice()
     {
-      return choice;
+      return ordinal() + 1;
     }
 
-    /**
-     * Return the menu message.
-     *
-     * @return the menu message.
-     */
-    public LocalizableMessage getMenuMessage()
+    private static TrustOption getTrustOptionForIndex(final int value)
     {
-      return msg;
+      for (final TrustOption trustOption : TrustOption.values())
+      {
+        if (trustOption.getChoice() == value)
+        {
+          return trustOption;
+        }
+      }
+      return null;
     }
   }
 
@@ -349,7 +292,7 @@
   {
     this.app = app;
     this.secureArgsList = secureArgs;
-    this.commandBuilder = new CommandBuilder(null, null);
+    this.commandBuilder = new CommandBuilder();
     state = new State(secureArgs);
     copySecureArgsList = new SecureConnectionCliArgs(secureArgs.alwaysSSL());
     try
@@ -387,447 +330,328 @@
    */
   public void run(boolean canUseStartTLS) throws ArgumentException
   {
-    // Reset everything
+    resetBeforeRun();
+    resolveHostName();
+    resolveConnectionType(canUseStartTLS);
+    resolvePortNumber();
+    resolveTrustAndKeyManagers();
+    resolveCredentialLogin();
+    resolveCredentialPassword();
+    resolveConnectTimeout();
+  }
+
+  private void resetBeforeRun() throws ArgumentException
+  {
     commandBuilder.clearArguments();
     copySecureArgsList.createGlobalArguments();
+    state.providedAdminUID = null;
+    state.providedBindDN = null;
+  }
 
-    boolean secureConnection = true;
+  private void resolveHostName() throws ArgumentException
+  {
+    state.hostName = secureArgsList.getHostNameArg().getValue();
+    promptForHostNameIfRequired();
+    addArgToCommandBuilder(copySecureArgsList.getHostNameArg(), state.hostName);
+  }
 
-    // Get the LDAP host.
-    state.hostName = secureArgsList.hostNameArg.getValue();
-    final String tmpHostName = state.hostName;
-    if (app.isInteractive() && !secureArgsList.hostNameArg.isPresent())
-    {
-      checkHeadingDisplayed();
+  private void resolveConnectionType(boolean canUseStartTLS)
+  {
+    state.setSsl(secureArgsList);
+    promptForConnectionTypeIfRequired(canUseStartTLS);
+    addConnectionTypeToCommandBuilder();
+  }
 
-      ValidationCallback<String> callback = new ValidationCallback<String>()
-      {
+  private void resolvePortNumber() throws ArgumentException
+  {
+    portNumber = (state.useSSL && !secureArgsList.getPortArg().isPresent())
+        ? secureArgsList.getPortFromConfig()
+        : secureArgsList.getPortArg().getIntValue();
+    promptForPortNumberIfRequired();
+    addArgToCommandBuilder(copySecureArgsList.getPortArg(), String.valueOf(portNumber));
+  }
 
-        @Override
-        public String validate(ConsoleApplication app, String input)
-            throws ClientException
-        {
-          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();
-        state.hostName = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_HOST_NAME.get(state.hostName), callback);
-      }
-      catch (ClientException e)
-      {
-        throw cannotReadConnectionParameters(e);
-      }
-    }
-
-    copySecureArgsList.hostNameArg.clearValues();
-    copySecureArgsList.hostNameArg.addValue(state.hostName);
-    commandBuilder.addArgument(copySecureArgsList.hostNameArg);
-
-    // Connection type
-    state.useSSL = secureArgsList.useSSL();
-    state.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<>(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 (!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()))
-          {
-            state.useSSL = true;
-          }
-          else if (result.getValue().equals(Protocols.START_TLS.getChoice()))
-          {
-            state.useStartTLS = true;
-          }
-        }
-        else
-        {
-          // Should never happen.
-          throw new RuntimeException();
-        }
-      }
-      catch (ClientException e)
-      {
-        throw new RuntimeException(e);
-      }
-    }
-
-    if (state.useSSL)
-    {
-      commandBuilder.addArgument(copySecureArgsList.useSSLArg);
-    }
-    else if (state.useStartTLS)
-    {
-      commandBuilder.addArgument(copySecureArgsList.useStartTLSArg);
-    }
-
-    // Get the LDAP port.
-    if (!state.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>()
-      {
-
-        @Override
-        public Integer validate(ConsoleApplication app, String input)
-            throws ClientException
-        {
-          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();
-        LocalizableMessage 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 (ClientException e)
-      {
-        throw cannotReadConnectionParameters(e);
-      }
-    }
-
-    copySecureArgsList.portArg.clearValues();
-    copySecureArgsList.portArg.addValue(String.valueOf(portNumber));
-    commandBuilder.addArgument(copySecureArgsList.portArg);
-
-    // Handle certificate
+  private void resolveTrustAndKeyManagers() throws ArgumentException {
     if ((state.useSSL || state.useStartTLS) && state.trustManager == null)
     {
-      initializeTrustManager();
+      initializeTrustAndKeyManagers();
+    }
+  }
+
+  private void resolveCredentialLogin() throws ArgumentException
+  {
+    setAdminUidAndBindDnFromArgs();
+    if (useKeyManager())
+    {
+      return;
+    }
+    promptForCredentialLoginIfRequired(secureArgsList.getBindDnArg().getValue(),
+                                       secureArgsList.getAdminUidArg().getValue());
+    final boolean onlyBindDnProvided = state.providedAdminUID != null || state.providedBindDN == null;
+    if ((useAdminOrBindDn && onlyBindDnProvided)
+     || (!useAdminOrBindDn && isAdminUidArgVisible()))
+    {
+      addArgToCommandBuilder(copySecureArgsList.getAdminUidArg(), getAdministratorUID());
+    }
+    else
+    {
+      addArgToCommandBuilder(copySecureArgsList.getBindDnArg(), getBindDN());
+    }
+  }
+
+  private void setAdminUidAndBindDnFromArgs()
+  {
+    final Argument adminUid = secureArgsList.getAdminUidArg();
+    final Argument bindDn = secureArgsList.getBindDnArg();
+
+    state.providedAdminUID = (isAdminUidArgVisible() && adminUid.isPresent()) ? adminUid.getValue() : null;
+    state.providedBindDN = ((useAdminOrBindDn || !isAdminUidArgVisible()) && bindDn.isPresent()) ? bindDn.getValue()
+                                                                                                 : null;
+    state.adminUID = !useKeyManager() ? adminUid.getValue() : null;
+    state.bindDN = !useKeyManager() ? bindDn.getValue() : null;
+  }
+
+  private void resolveCredentialPassword() throws ArgumentException
+  {
+    if (secureArgsList.getBindPasswordArg().isPresent())
+    {
+      state.bindPassword = secureArgsList.getBindPasswordArg().getValue();
     }
 
-    // Get the LDAP bind credentials.
-    state.bindDN = secureArgsList.bindDnArg.getValue();
-    state.adminUID= secureArgsList.adminUidArg.getValue();
-    final boolean useAdmin = secureArgsList.useAdminUID();
-    if (useAdmin && secureArgsList.adminUidArg.isPresent())
+    if (useKeyManager())
     {
-      state.providedAdminUID = state.adminUID;
+      return;
     }
-    else
+
+    setBindPasswordFileFromArgs();
+    final boolean addedPasswordFileArgument = secureArgsList.getBindPasswordFileArg().isPresent();
+    if (!addedPasswordFileArgument && (state.bindPassword == null || "-".equals(state.bindPassword)))
     {
-      state.providedAdminUID = null;
+      promptForBindPasswordIfRequired();
     }
-    if ((!useAdmin || useAdminOrBindDn) && secureArgsList.bindDnArg.isPresent())
+
+    final Argument bindPassword = copySecureArgsList.getBindPasswordArg();
+    bindPassword.clearValues();
+    bindPassword.addValue(state.bindPassword);
+    if (!addedPasswordFileArgument)
     {
-      state.providedBindDN = state.bindDN;
+      commandBuilder.addObfuscatedArgument(bindPassword);
     }
-    else
+  }
+
+  private void setBindPasswordFileFromArgs() throws ArgumentException
+  {
+    final FileBasedArgument bindPasswordFile = secureArgsList.getBindPasswordFileArg();
+    if (bindPasswordFile.isPresent())
     {
-      state.providedBindDN = null;
-    }
-    boolean argIsPresent = state.providedAdminUID != null || state.providedBindDN != null;
-    final String tmpBindDN = state.bindDN;
-    final String tmpAdminUID = state.adminUID;
-    if (state.keyManager == null)
-    {
-      if (app.isInteractive() && !argIsPresent)
+      // Read from file if it exists.
+      state.bindPassword = bindPasswordFile.getValue();
+      if (state.bindPassword == null)
       {
-        checkHeadingDisplayed();
+        throw new ArgumentException(
+            ERR_ERROR_NO_ADMIN_PASSWORD.get(isAdminUidArgVisible() ? state.adminUID : state.bindDN));
+      }
+      addArgToCommandBuilder(copySecureArgsList.getBindPasswordFileArg(), bindPasswordFile.getNameToValueMap());
+    }
+  }
 
-        ValidationCallback<String> callback = new ValidationCallback<String>()
+  private void resolveConnectTimeout() throws ArgumentException
+  {
+    state.connectTimeout = secureArgsList.getConnectTimeoutArg().getIntValue();
+  }
+
+  private void promptForHostNameIfRequired() throws ArgumentException
+  {
+    if (!app.isInteractive() || secureArgsList.getHostNameArg().isPresent())
+    {
+      return;
+    }
+    checkHeadingDisplayed();
+    ValidationCallback<String> callback = new ValidationCallback<String>()
+    {
+      @Override
+      public String validate(ConsoleApplication app, String rawInput) throws ClientException
+      {
+        final String input = rawInput.trim();
+        if (input.length() == 0)
         {
-
-          @Override
-          public String validate(ConsoleApplication app, String input)
-              throws ClientException
-          {
-            String ninput = input.trim();
-            if (ninput.length() == 0)
-            {
-              if (useAdmin)
-              {
-                return tmpAdminUID;
-              }
-              else
-              {
-                return tmpBindDN;
-              }
-            }
-            else
-            {
-              return ninput;
-            }
-          }
-
-        };
+          return state.hostName;
+        }
 
         try
         {
-          app.println();
-          if (useAdminOrBindDn)
-          {
-            String def = state.adminUID != null ? state.adminUID : state.bindDN;
-            String v =
-                app.readValidatedInput(
-                    INFO_LDAP_CONN_GLOBAL_ADMINISTRATOR_OR_BINDDN_PROMPT.get(def), callback);
-            if (isDN(v))
-            {
-              state.bindDN = v;
-              state.providedBindDN = v;
-              state.adminUID = null;
-              state.providedAdminUID = null;
-            }
-            else
-            {
-              state.bindDN = null;
-              state.providedBindDN = null;
-              state.adminUID = v;
-              state.providedAdminUID = v;
-            }
-          }
-          else if (useAdmin)
-          {
-            state.adminUID =
-                app.readValidatedInput(INFO_LDAP_CONN_PROMPT_ADMINISTRATOR_UID.get(state.adminUID), callback);
-            state.providedAdminUID = state.adminUID;
-          }
-          else
-          {
-            state.bindDN =
-                app.readValidatedInput(INFO_LDAP_CONN_PROMPT_BIND_DN.get(state.bindDN), callback);
-            state.providedBindDN = state.bindDN;
-          }
+          // Ensure that the prompted host is known
+          InetAddress.getByName(input);
+          return input;
         }
-        catch (ClientException e)
+        catch (UnknownHostException e)
         {
-          throw cannotReadConnectionParameters(e);
+          // Try again...
+          app.println();
+          app.println(ERR_LDAP_CONN_BAD_HOST_NAME.get(input));
+          app.println();
+          return null;
         }
       }
+    };
+
+    try
+    {
+      app.println();
+      state.hostName = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_HOST_NAME.get(state.hostName), callback);
+    }
+    catch (ClientException e)
+    {
+      throw cannotReadConnectionParameters(e);
+    }
+  }
+
+  private void promptForConnectionTypeIfRequired(final boolean canUseStartTLS)
+  {
+    final boolean valuesSetByProperty = secureArgsList.getUseSSLArg().isValueSetByProperty()
+                                     && secureArgsList.getUseStartTLSArg().isValueSetByProperty();
+    if (!app.isInteractive() || state.useSSL || state.useStartTLS || valuesSetByProperty)
+    {
+      return;
+    }
+    checkHeadingDisplayed();
+    final MenuBuilder<Integer> builder = new MenuBuilder<>(app);
+    builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_USE_SECURE_CTX.get());
+
+    for (Protocol p : Protocol.values())
+    {
+      if ((!displayLdapIfSecureParameters && Protocol.LDAP.equals(p))
+          || (!canUseStartTLS && Protocol.START_TLS.equals(p)))
+      {
+        continue;
+      }
+
+      final MenuResult<Integer> menuResult = MenuResult.success(p.getChoice());
+      final int i = builder.addNumberedOption(p.message, menuResult);
+      if (DEFAULT_PROMPT_PROTOCOL.equals(p))
+      {
+        builder.setDefault(INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE.get(i), menuResult);
+      }
+    }
+
+    Menu<Integer> menu = builder.toMenu();
+    try
+    {
+      final MenuResult<Integer> result = menu.run();
+      throwIfMenuResultNotSucceeded(result);
+      final int userChoice = result.getValue();
+      if (Protocol.SSL.getChoice() == userChoice)
+      {
+        state.useSSL = true;
+      }
+      else if (Protocol.START_TLS.getChoice() == userChoice)
+      {
+        state.useStartTLS = true;
+      }
+    }
+    catch (ClientException e)
+    {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private void promptForPortNumberIfRequired() throws ArgumentException
+  {
+    if (!app.isInteractive() || secureArgsList.getPortArg().isPresent())
+    {
+      return;
+    }
+    checkHeadingDisplayed();
+    try
+    {
+      app.println();
+      final LocalizableMessage askPortNumberMsg = secureArgsList.alwaysSSL() ?
+          INFO_ADMIN_CONN_PROMPT_PORT_NUMBER.get(portNumber) :
+          INFO_LDAP_CONN_PROMPT_PORT_NUMBER.get(portNumber);
+      portNumber = app.readValidatedInput(askPortNumberMsg, portValidationCallback(portNumber));
+    }
+    catch (ClientException e)
+    {
+      throw cannotReadConnectionParameters(e);
+    }
+  }
+
+  private void promptForCredentialLoginIfRequired(final String defaultBindDN, final String defaultAdminUID)
+      throws ArgumentException
+  {
+    if (!app.isInteractive() || state.providedAdminUID != null || state.providedBindDN != null)
+    {
+      return;
+    }
+    checkHeadingDisplayed();
+    ValidationCallback<String> callback = new ValidationCallback<String>()
+    {
+      @Override public String validate(ConsoleApplication app, String rawInput) throws ClientException
+      {
+        final String input = rawInput.trim();
+        if (input.isEmpty())
+        {
+          return isAdminUidArgVisible() ? defaultAdminUID : defaultBindDN;
+        }
+
+        return input;
+      }
+    };
+
+    try
+    {
+      app.println();
       if (useAdminOrBindDn)
       {
-        boolean addAdmin = state.providedAdminUID != null;
-        boolean addBindDN = state.providedBindDN != null;
-        if (!addAdmin && !addBindDN)
+        String def = state.adminUID != null ? state.adminUID : state.bindDN;
+        String v = app.readValidatedInput(INFO_LDAP_CONN_GLOBAL_ADMINISTRATOR_OR_BINDDN_PROMPT.get(def), callback);
+        if (isDN(v))
         {
-          addAdmin = getAdministratorUID() != null;
-          addBindDN = getBindDN() != null;
+          state.bindDN = v;
+          state.providedBindDN = v;
+          state.adminUID = null;
+          state.providedAdminUID = null;
         }
-        if (addAdmin)
+        else
         {
-          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);
+          state.bindDN = null;
+          state.providedBindDN = null;
+          state.adminUID = v;
+          state.providedAdminUID = v;
         }
       }
-      else if (useAdmin)
+      else if (isAdminUidArgVisible())
       {
-        copySecureArgsList.adminUidArg.clearValues();
-        copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
-        commandBuilder.addArgument(copySecureArgsList.adminUidArg);
+        state.adminUID = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_ADMINISTRATOR_UID.get(state.adminUID), callback);
+        state.providedAdminUID = state.adminUID;
       }
       else
       {
-        copySecureArgsList.bindDnArg.clearValues();
-        copySecureArgsList.bindDnArg.addValue(getBindDN());
-        commandBuilder.addArgument(copySecureArgsList.bindDnArg);
+        state.bindDN = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_BIND_DN.get(state.bindDN), callback);
+        state.providedBindDN = state.bindDN;
       }
     }
-    else
+    catch (ClientException e)
     {
-      state.bindDN = null;
-      state.adminUID = null;
+      throw cannotReadConnectionParameters(e);
     }
-
-    boolean addedPasswordFileArgument = false;
-    if (secureArgsList.bindPasswordArg.isPresent())
-    {
-      state.bindPassword = secureArgsList.bindPasswordArg.getValue();
-    }
-    if (state.keyManager == null)
-    {
-      if (secureArgsList.bindPasswordFileArg.isPresent())
-      {
-        // Read from file if it exists.
-        state.bindPassword = secureArgsList.bindPasswordFileArg.getValue();
-
-        if (state.bindPassword == null)
-        {
-          if (useAdmin)
-          {
-            throw new ArgumentException(ERR_ERROR_NO_ADMIN_PASSWORD.get(state.adminUID));
-          }
-          else
-          {
-            throw new ArgumentException(ERR_ERROR_NO_ADMIN_PASSWORD.get(state.bindDN));
-          }
-        }
-        copySecureArgsList.bindPasswordFileArg.clearValues();
-        copySecureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(
-            secureArgsList.bindPasswordFileArg.getNameToValueMap());
-        commandBuilder.addArgument(copySecureArgsList.bindPasswordFileArg);
-        addedPasswordFileArgument = true;
-      }
-      else if (state.bindPassword == null || "-".equals(state.bindPassword))
-      {
-        // Read the password from the stdin.
-        if (!app.isInteractive())
-        {
-          throw new ArgumentException(ERR_ERROR_BIND_PASSWORD_NONINTERACTIVE.get());
-        }
-
-        checkHeadingDisplayed();
-
-        try
-        {
-          app.println();
-          state.bindPassword = readPassword(state.getPrompt());
-        }
-        catch (Exception e)
-        {
-          throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
-        }
-      }
-      copySecureArgsList.bindPasswordArg.clearValues();
-      copySecureArgsList.bindPasswordArg.addValue(state.bindPassword);
-      if (!addedPasswordFileArgument)
-      {
-        commandBuilder.addObfuscatedArgument(copySecureArgsList.bindPasswordArg);
-      }
-    }
-    state.connectTimeout = secureArgsList.connectTimeoutArg.getIntValue();
   }
 
-  private ArgumentException cannotReadConnectionParameters(ClientException e)
+  private void promptForBindPasswordIfRequired() throws ArgumentException
   {
-    return new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
-  }
-
-  private String readPassword(LocalizableMessage prompt) throws ClientException
-  {
-    final char[] pwd = app.readPassword(prompt);
-    if (pwd != null)
+    if (!app.isInteractive())
     {
-      return String.valueOf(pwd);
+      throw new ArgumentException(ERR_ERROR_BIND_PASSWORD_NONINTERACTIVE.get());
     }
-    return null;
+    checkHeadingDisplayed();
+    try
+    {
+      state.bindPassword = readPassword(state.getPrompt());
+    }
+    catch (Exception e)
+    {
+      throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
+    }
   }
 
   /**
@@ -837,205 +661,90 @@
    * @throws ArgumentException
    *           If an error occurs when getting args values.
    */
-  private ApplicationTrustManager getTrustManagerInternal()
-      throws ArgumentException
+  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);
+    commandBuilder.removeArguments(copySecureArgsList.getTrustAllArg(),
+                                   copySecureArgsList.getTrustStorePathArg(),
+                                   copySecureArgsList.getTrustStorePasswordArg(),
+                                   copySecureArgsList.getTrustStorePasswordFileArg());
 
-    // If we have the trustALL flag, don't do anything
-    // just return null
-    if (secureArgsList.trustAllArg.isPresent())
+    final TrustMethod trustMethod = resolveTrustMethod();
+    if (TrustMethod.TRUSTALL == trustMethod)
     {
-      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;
-
-    state.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 && addLocalTrustStore())
-    {
-      weDontKnowTheTrustMethod = false;
-    }
-
-    if (app.isInteractive() && weDontKnowTheTrustMethod)
-    {
-      checkHeadingDisplayed();
-
-      app.println();
-      MenuBuilder<Integer> builder = new MenuBuilder<>(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(Integer.valueOf(i)), MenuResult.success(t.getChoice()));
-        }
-      }
-
-      Menu<Integer> menu = builder.toMenu();
-      state.trustStoreInMemory = false;
-      try
-      {
-        MenuResult<Integer> result = menu.run();
-        if (result.isSuccess())
-        {
-          if (result.getValue().equals(TrustMethod.TRUSTALL.getChoice()))
-          {
-            commandBuilder.addArgument(copySecureArgsList.trustAllArg);
-            state.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;
-            state.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 (ClientException 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.
-    state.truststorePath = secureArgsList.trustStorePathArg.getValue();
-
-    if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent() && askForTrustStore)
-    {
-      checkHeadingDisplayed();
-
-      ValidationCallback<String> callback = new ValidationCallback<String>()
-      {
-        @Override
-        public String validate(ConsoleApplication app, String input)
-            throws ClientException
-        {
-          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();
-        state.truststorePath = app.readValidatedInput(
-                INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(), callback);
-      }
-      catch (ClientException e)
-      {
-        throw cannotReadConnectionParameters(e);
-      }
-    }
-
-    if (state.truststorePath != null)
-    {
-      copySecureArgsList.trustStorePathArg.clearValues();
-      copySecureArgsList.trustStorePathArg.addValue(state.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.
-    if (secureArgsList.trustStorePasswordArg.isPresent())
-    {
-      state.truststorePassword = secureArgsList.trustStorePasswordArg.getValue();
-    }
-    if (secureArgsList.trustStorePasswordFileArg.isPresent())
-    {
-      // Read from file if it exists.
-      state.truststorePassword = secureArgsList.trustStorePasswordFileArg.getValue();
-    }
+    final boolean promptForTrustStore = TrustMethod.TRUSTSTORE == trustMethod;
+    resolveTrustStorePath(promptForTrustStore);
+    setTrustStorePassword();
+    setTrustStorePasswordFromFile();
     if ("-".equals(state.truststorePassword))
     {
       // Read the password from the stdin.
-      if (!app.isInteractive())
-      {
-        state.truststorePassword = null;
-      }
-      else
-      {
-        checkHeadingDisplayed();
-
-        try
-        {
-          app.println();
-          LocalizableMessage prompt = INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD.get(state.truststorePath);
-          state.truststorePassword = readPassword(prompt);
-        }
-        catch (Exception e)
-        {
-          throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
-        }
-      }
+      promptForTrustStorePasswordIfRequired();
     }
 
-    // We've got all the information to get the truststore manager
+    return resolveTrustStore();
+  }
+
+  /** As the most common case is to have no password for trust store, we do not ask it in the interactive mode.*/
+  private void setTrustStorePassword()
+  {
+    if (secureArgsList.getTrustStorePasswordArg().isPresent())
+    {
+      state.truststorePassword = secureArgsList.getTrustStorePasswordArg().getValue();
+    }
+  }
+
+  private void setTrustStorePasswordFromFile()
+  {
+    if (secureArgsList.getTrustStorePasswordFileArg().isPresent())
+    {
+      state.truststorePassword = secureArgsList.getTrustStorePasswordFileArg().getValue();
+    }
+  }
+
+  /** Return the trust method chosen by user or {@code null} if the information is not available. */
+  private TrustMethod resolveTrustMethod()
+  {
+    state.trustAll = secureArgsList.getTrustAllArg().isPresent();
+    // Check if some trust manager info are set
+    boolean needPromptForTrustMethod = !state.trustAll
+        && !secureArgsList.getTrustStorePathArg().isPresent()
+        && !secureArgsList.getTrustStorePasswordArg().isPresent()
+        && !secureArgsList.getTrustStorePasswordFileArg().isPresent();
+
+    TrustMethod trustMethod = state.trustAll ? TrustMethod.TRUSTALL : null;
+    // 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 (needPromptForTrustMethod && !useLocalTrustStoreIfPossible())
+    {
+      trustMethod = promptForTrustMethodIfRequired();
+    }
+
+    if (trustMethod != TrustMethod.TRUSTSTORE)
+    {
+      // There is no direct equivalent for the display certificate option,
+      // so propose trust all option as command-line argument.
+      commandBuilder.addArgument(copySecureArgsList.getTrustAllArg());
+    }
+
+    return trustMethod;
+  }
+
+  private void resolveTrustStorePath(final boolean promptForTrustStore) throws ArgumentException
+  {
+    state.truststorePath = secureArgsList.getTrustStorePathArg().getValue();
+    if (promptForTrustStore)
+    {
+      promptForTrustStorePathIfRequired();
+    }
+    addArgToCommandBuilder(copySecureArgsList.getTrustStorePathArg(), state.truststorePath);
+  }
+
+  private ApplicationTrustManager resolveTrustStore() throws ArgumentException
+  {
     try
     {
       state.truststore = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -1043,14 +752,7 @@
       {
         try (FileInputStream fos = new FileInputStream(state.truststorePath))
         {
-          if (state.truststorePassword != null)
-          {
-            state.truststore.load(fos, state.truststorePassword.toCharArray());
-          }
-          else
-          {
-            state.truststore.load(fos, null);
-          }
+          state.truststore.load(fos, state.truststorePassword != null ? state.truststorePassword.toCharArray() : null);
         }
       }
       else
@@ -1058,20 +760,14 @@
         state.truststore.load(null, null);
       }
 
-      if (secureArgsList.trustStorePasswordFileArg.isPresent() && state.truststorePath != null)
+      if (secureArgsList.getTrustStorePasswordFileArg().isPresent() && state.truststorePath != null)
       {
-        copySecureArgsList.trustStorePasswordFileArg.clearValues();
-        copySecureArgsList.trustStorePasswordFileArg.getNameToValueMap()
-            .putAll(secureArgsList.trustStorePasswordFileArg.getNameToValueMap());
-        commandBuilder.addArgument(copySecureArgsList.trustStorePasswordFileArg);
+        addArgToCommandBuilder(copySecureArgsList.getTrustStorePasswordFileArg(),
+            secureArgsList.getTrustStorePasswordFileArg().getNameToValueMap());
       }
       else if (state.truststorePassword != null && state.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(state.truststorePassword);
-        commandBuilder.addObfuscatedArgument(copySecureArgsList.trustStorePasswordArg);
+        addObfuscatedArgToCommandBuilder(copySecureArgsList.getTrustStorePasswordArg(), state.truststorePassword);
       }
 
       return new ApplicationTrustManager(state.truststore);
@@ -1082,6 +778,91 @@
     }
   }
 
+  private TrustMethod promptForTrustMethodIfRequired()
+  {
+    if (!app.isInteractive())
+    {
+      return null;
+    }
+
+    checkHeadingDisplayed();
+    app.println();
+    MenuBuilder<Integer> builder = new MenuBuilder<>(app);
+    builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_METHOD.get());
+
+    for (TrustMethod t : TrustMethod.values())
+    {
+      int i = builder.addNumberedOption(t.message, MenuResult.success(t.getChoice()));
+      if (DEFAULT_PROMPT_TRUST_METHOD.equals(t))
+      {
+        builder.setDefault(
+            INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE.get(i), MenuResult.success(t.getChoice()));
+      }
+    }
+
+    Menu<Integer> menu = builder.toMenu();
+    state.trustStoreInMemory = false;
+    try
+    {
+      final MenuResult<Integer> result = menu.run();
+      throwIfMenuResultNotSucceeded(result);
+      final int userChoice = result.getValue();
+      if (TrustMethod.TRUSTALL.getChoice() == userChoice)
+      {
+        state.trustAll = true;
+      }
+      else if (TrustMethod.DISPLAY_CERTIFICATE.getChoice() == userChoice)
+      {
+        state.trustStoreInMemory = true;
+      }
+      return TrustMethod.getTrustMethodForIndex(userChoice);
+    }
+    catch (ClientException e)
+    {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private void promptForTrustStorePathIfRequired() throws ArgumentException
+  {
+    if (!app.isInteractive() || secureArgsList.getTrustStorePathArg().isPresent())
+    {
+      return;
+    }
+
+    checkHeadingDisplayed();
+    try
+    {
+      app.println();
+      state.truststorePath = app.readValidatedInput(
+          INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(),
+          filePathValidationCallback(!ALLOW_EMPTY_PATH, FILE_MUST_EXISTS));
+    }
+    catch (ClientException e)
+    {
+      throw cannotReadConnectionParameters(e);
+    }
+  }
+
+  private void promptForTrustStorePasswordIfRequired() throws ArgumentException
+  {
+    if (!app.isInteractive())
+    {
+      return;
+    }
+
+    checkHeadingDisplayed();
+    try
+    {
+      state.truststorePassword = readPassword(
+          INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD.get(state.truststorePath));
+    }
+    catch (Exception e)
+    {
+      throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
+    }
+  }
+
   /**
    * Get the key manager.
    *
@@ -1092,218 +873,283 @@
   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);
+    commandBuilder.removeArguments(copySecureArgsList.getCertNicknameArg(),
+                                   copySecureArgsList.getKeyStorePathArg(),
+                                   copySecureArgsList.getKeyStorePasswordArg(),
+                                   copySecureArgsList.getKeyStorePasswordFileArg());
 
-    // 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)
+    if (!secureArgsList.getKeyStorePathArg().isPresent()
+     && !secureArgsList.getKeyStorePasswordArg().isPresent()
+     && !secureArgsList.getKeyStorePasswordFileArg().isPresent()
+     && !secureArgsList.getCertNicknameArg().isPresent())
     {
+      // If no one of these parameters above are set, we assume that we do not need client side authentication.
+      // Client side authentication is not the common use case so interactive mode doesn't add an extra question.
       return null;
     }
 
-    // Get info about keystore. First get the keystore path.
-    state.keystorePath = secureArgsList.keyStorePathArg.getValue();
-    if (app.isInteractive() && !secureArgsList.keyStorePathArg.isPresent())
+    resolveKeyStorePath();
+    resolveKeyStorePassword();
+
+    final KeyStore keystore = createKeyStore();
+    resolveCertificateNickname(keystore);
+
+    final ApplicationKeyManager keyManager = new ApplicationKeyManager(keystore, state.keystorePassword.toCharArray());
+    addKeyStorePasswordArgToCommandBuilder();
+    if (state.certifNickname != null)
     {
-      checkHeadingDisplayed();
-
-      ValidationCallback<String> callback = new ValidationCallback<String>()
-      {
-        @Override
-        public String validate(ConsoleApplication app, String input)
-            throws ClientException
-        {
-          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();
-        state.keystorePath = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH.get(), callback);
-      }
-      catch (ClientException e)
-      {
-        throw cannotReadConnectionParameters(e);
-      }
+      addArgToCommandBuilder(copySecureArgsList.getCertNicknameArg(), state.certifNickname);
+      return SelectableCertificateKeyManager.wrap(
+          new KeyManager[] { keyManager }, CollectionUtils.newTreeSet(state.certifNickname))[0];
     }
 
-    if (state.keystorePath != null)
+    return keyManager;
+  }
+
+  private void resolveKeyStorePath() throws ArgumentException
+  {
+    state.keyStorePath = secureArgsList.getKeyStorePathArg().getValue();
+    promptForKeyStorePathIfRequired();
+
+    if (state.keyStorePath == null)
     {
-      copySecureArgsList.keyStorePathArg.clearValues();
-      copySecureArgsList.keyStorePathArg.addValue(state.keystorePath);
-      commandBuilder.addArgument(copySecureArgsList.keyStorePathArg);
-    }
-    else
-    {
-      // KeystorePath is null. Either it's unspecified or there's a pb
-      // We should throw an exception here, anyway since code below will
-      // anyway
       throw new ArgumentException(ERR_ERROR_INCOMPATIBLE_PROPERTY_MOD.get("null keystorePath"));
     }
+    addArgToCommandBuilder(copySecureArgsList.getKeyStorePathArg(), state.keyStorePath);
+  }
 
-    // Then the keystore password.
-    state.keystorePassword = secureArgsList.keyStorePasswordArg.getValue();
+  private void resolveKeyStorePassword() throws ArgumentException
+  {
+    state.keystorePassword = secureArgsList.getKeyStorePasswordArg().getValue();
 
-    if (secureArgsList.keyStorePasswordFileArg.isPresent())
+    if (secureArgsList.getKeyStorePasswordFileArg().isPresent())
     {
-      // Read from file if it exists.
-      state.keystorePassword = secureArgsList.keyStorePasswordFileArg.getValue();
-
+      state.keystorePassword = secureArgsList.getKeyStorePasswordFileArg().getValue();
       if (state.keystorePassword == null)
       {
-        throw new ArgumentException(ERR_ERROR_NO_ADMIN_PASSWORD.get(state.keystorePassword));
+        throw new ArgumentException(ERR_INSTALLDS_NO_KEYSTORE_PASSWORD.get(
+            secureArgsList.getKeyStorePathArg().getLongIdentifier(),
+            secureArgsList.getKeyStorePasswordFileArg().getLongIdentifier()));
       }
     }
     else if (state.keystorePassword == null || "-".equals(state.keystorePassword))
     {
-      // Read the password from the stdin.
-      if (!app.isInteractive())
-      {
-        throw new ArgumentException(ERR_ERROR_BIND_PASSWORD_NONINTERACTIVE.get());
-      }
-
-      checkHeadingDisplayed();
-
-      try
-      {
-        app.println();
-        LocalizableMessage prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD.get(state.keystorePath);
-        state.keystorePassword = readPassword(prompt);
-      }
-      catch (Exception e)
-      {
-        throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
-      }
+      promptForKeyStorePasswordIfRequired();
     }
+  }
 
-    // finally the certificate name, if needed.
-    KeyStore keystore = null;
-    Enumeration<String> aliasesEnum = null;
-    try (FileInputStream fos = new FileInputStream(state.keystorePath))
+  private KeyStore createKeyStore() throws ArgumentException
+  {
+    try (FileInputStream fos = new FileInputStream(state.keyStorePath))
     {
-      keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+      final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
       keystore.load(fos, state.keystorePassword.toCharArray());
-      aliasesEnum = keystore.aliases();
+      return keystore;
     }
     catch (Exception e)
     {
       throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
     }
+  }
 
-    state.certifNickname = secureArgsList.certNicknameArg.getValue();
-    if (app.isInteractive() && !secureArgsList.certNicknameArg.isPresent() && aliasesEnum.hasMoreElements())
+  private void resolveCertificateNickname(final KeyStore keystore) throws ArgumentException
+  {
+    state.certifNickname = secureArgsList.getCertNicknameArg().getValue();
+    try
     {
-      checkHeadingDisplayed();
+      promptForCertificateNicknameIfRequired(keystore, keystore.aliases());
+    }
+    catch (final KeyStoreException e)
+    {
+      throw new ArgumentException(ERR_RESOLVE_KEYSTORE_ALIASES.get(e.getMessage()), e);
+    }
+  }
 
-      try
+  private void promptForKeyStorePathIfRequired() throws ArgumentException
+  {
+    if (!app.isInteractive() || secureArgsList.getKeyStorePathArg().isPresent())
+    {
+      return;
+    }
+    checkHeadingDisplayed();
+    try
+    {
+      app.println();
+      state.keyStorePath = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH.get(),
+                                                  filePathValidationCallback(ALLOW_EMPTY_PATH, FILE_MUST_EXISTS));
+    }
+    catch (ClientException e)
+    {
+      throw cannotReadConnectionParameters(e);
+    }
+  }
+
+  private void promptForKeyStorePasswordIfRequired() throws ArgumentException
+  {
+    if (!app.isInteractive())
+    {
+      throw new ArgumentException(ERR_ERROR_BIND_PASSWORD_NONINTERACTIVE.get());
+    }
+    checkHeadingDisplayed();
+    try
+    {
+      state.keystorePassword = readPassword(INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD.get(state.keyStorePath));
+    }
+    catch (Exception e)
+    {
+      throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
+    }
+  }
+
+  private void promptForCertificateNicknameIfRequired(KeyStore keystore, Enumeration<String> aliasesEnum)
+      throws ArgumentException
+  {
+    if (!app.isInteractive() || secureArgsList.getCertNicknameArg().isPresent() || !aliasesEnum.hasMoreElements())
+    {
+      return;
+    }
+    state.certifNickname = null;
+    checkHeadingDisplayed();
+    try
+    {
+      MenuBuilder<String> builder = new MenuBuilder<>(app);
+      builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIASES.get());
+      int certificateNumber = 0;
+      while (aliasesEnum.hasMoreElements())
       {
-        MenuBuilder<String> builder = new MenuBuilder<>(app);
-        builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIASES.get());
-        int certificateNumber = 0;
-        for (; aliasesEnum.hasMoreElements();)
+        final String alias = aliasesEnum.nextElement();
+        if (keystore.isKeyEntry(alias))
         {
-          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())
-          {
-            state.certifNickname = result.getValue();
-          }
-          else
-          {
-            // Should never happen.
-            throw new RuntimeException();
-          }
-        }
-        else
-        {
-          state.certifNickname = null;
+          certificateNumber++;
+          X509Certificate certif = (X509Certificate) keystore.getCertificate(alias);
+          builder.addNumberedOption(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIAS.get(
+                                                                                alias, certif.getSubjectDN().getName()),
+                                    MenuResult.success(alias));
         }
       }
-      catch (KeyStoreException e)
+
+      if (certificateNumber > 1)
       {
-        throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
-      }
-      catch (ClientException e)
-      {
-        throw cannotReadConnectionParameters(e);
+        app.println();
+        Menu<String> menu = builder.toMenu();
+        final MenuResult<String> result = menu.run();
+        throwIfMenuResultNotSucceeded(result);
+        state.certifNickname = result.getValue();
       }
     }
-
-    // We'we got all the information to get the keys manager
-    ApplicationKeyManager akm =
-        new ApplicationKeyManager(keystore, state.keystorePassword.toCharArray());
-
-    if (secureArgsList.keyStorePasswordFileArg.isPresent())
+    catch (KeyStoreException e)
     {
-      copySecureArgsList.keyStorePasswordFileArg.clearValues();
-      copySecureArgsList.keyStorePasswordFileArg.getNameToValueMap().putAll(
-          secureArgsList.keyStorePasswordFileArg.getNameToValueMap());
-      commandBuilder.addArgument(copySecureArgsList.keyStorePasswordFileArg);
+      throw new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
+    }
+    catch (ClientException e)
+    {
+      throw cannotReadConnectionParameters(e);
+    }
+  }
+
+  private void addKeyStorePasswordArgToCommandBuilder()
+  {
+    if (secureArgsList.getKeyStorePasswordFileArg().isPresent())
+    {
+      addArgToCommandBuilder(copySecureArgsList.getKeyStorePasswordFileArg(),
+          secureArgsList.getKeyStorePasswordFileArg().getNameToValueMap());
     }
     else if (state.keystorePassword != null)
     {
-      copySecureArgsList.keyStorePasswordArg.clearValues();
-      copySecureArgsList.keyStorePasswordArg.addValue(state.keystorePassword);
-      commandBuilder.addObfuscatedArgument(copySecureArgsList.keyStorePasswordArg);
+      addObfuscatedArgToCommandBuilder(copySecureArgsList.getKeyStorePasswordArg(), state.keystorePassword);
     }
+  }
 
-    if (state.certifNickname != null)
+  private void addConnectionTypeToCommandBuilder()
+  {
+    if (state.useSSL)
     {
-      copySecureArgsList.certNicknameArg.clearValues();
-      copySecureArgsList.certNicknameArg.addValue(state.certifNickname);
-      return SelectableCertificateKeyManager.wrap(
-          new KeyManager[] { akm },
-          CollectionUtils.newTreeSet(state.certifNickname))[0];
+      commandBuilder.addArgument(copySecureArgsList.getUseSSLArg());
     }
-    return akm;
+    else if (state.useStartTLS)
+    {
+      commandBuilder.addArgument(copySecureArgsList.getUseStartTLSArg());
+    }
+  }
+
+  private void addArgToCommandBuilder(final Argument arg, final String value)
+  {
+    addArgToCommandBuilder(arg, value, false);
+  }
+
+  private void addObfuscatedArgToCommandBuilder(final Argument arg, final String value)
+  {
+    addArgToCommandBuilder(arg, value, true);
+  }
+
+  private void addArgToCommandBuilder(final Argument arg, final String value, final boolean obfuscated)
+  {
+    if (value != null)
+    {
+      arg.clearValues();
+      arg.addValue(value);
+      commandBuilder.addArgument(arg);
+    }
+  }
+
+  private void addArgToCommandBuilder(final FileBasedArgument arg, final Map<String, String> nameToValueMap)
+  {
+    arg.clearValues();
+    arg.getNameToValueMap().putAll(nameToValueMap);
+    commandBuilder.addArgument(arg);
+  }
+
+  private ArgumentException cannotReadConnectionParameters(ClientException e)
+  {
+    return new ArgumentException(ERR_ERROR_CANNOT_READ_CONNECTION_PARAMETERS.get(e.getMessage()), e.getCause());
+  }
+
+  private String readPassword(LocalizableMessage prompt) throws ClientException
+  {
+    app.println();
+    final char[] pwd = app.readPassword(prompt);
+    if (pwd != null)
+    {
+      return String.valueOf(pwd);
+    }
+    return null;
+  }
+
+  private ValidationCallback<String> filePathValidationCallback(
+      final boolean allowEmptyPath, final boolean checkExistenceAndReadability)
+  {
+    return new ValidationCallback<String>()
+    {
+      @Override
+      public String validate(final ConsoleApplication app, final String filePathUserInput) throws ClientException
+      {
+        final String filePath = filePathUserInput.trim();
+        final File f = new File(filePath);
+
+        if ((!allowEmptyPath && filePath.isEmpty())
+            || f.isDirectory()
+            || (checkExistenceAndReadability && !(f.exists() && f.canRead())))
+        {
+          app.println();
+          app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH.get());
+          app.println();
+          return null;
+        }
+
+        return filePath;
+      }
+    };
+  }
+
+  /** Returns {@code true} if client script uses the adminUID argument. */
+  private boolean isAdminUidArgVisible()
+  {
+    return !secureArgsList.getAdminUidArg().isHidden();
+  }
+
+  private boolean useKeyManager()
+  {
+    return state.keyManager != null;
   }
 
   /**
@@ -1374,10 +1220,6 @@
     {
       return state.getAdminOrBindDN();
     }
-    else if (secureArgsList.useAdminUID())
-    {
-      return getAdministratorDN(state.adminUID);
-    }
     else
     {
       return state.bindDN;
@@ -1480,219 +1322,196 @@
    *          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)
+  public boolean checkServerCertificate(final X509Certificate[] chain, final String authType, final String host)
   {
     if (state.trustManager == null)
     {
       try
       {
-        initializeTrustManager();
+        initializeTrustAndKeyManagers();
       }
       catch (ArgumentException ae)
       {
-        // Should not occur
+        // Should not append because this.run() should has been called at this stage.
         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()));
-
-      // certificate validity
-      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY.get(
-          chain[i].getNotBefore(), chain[i].getNotAfter()));
-
-      // certificate Issuer
-      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER.get(chain[i].getIssuerDN()));
-
-      if (i + 1 < chain.length)
-      {
-        app.println();
-        app.println();
-      }
-    }
+    printCertificateChain(chain);
     MenuBuilder<Integer> builder = new MenuBuilder<>(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))
+      final MenuResult<Integer> result = MenuResult.success(t.getChoice());
+      int i = builder.addNumberedOption(t.message, result);
+      if (DEFAULT_PROMPT_TRUST_OPTION.equals(t))
       {
-        builder.setDefault(INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE.get(
-            Integer.valueOf(i)), MenuResult.success(t.getChoice()));
+        builder.setDefault(INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE.get(i), result);
       }
     }
 
     app.println();
     app.println();
 
-    Menu<Integer> menu = builder.toMenu();
-    while (true)
+    final Menu<Integer> menu = builder.toMenu();
+    try
     {
-      try
+      boolean promptAgain;
+      int userChoice;
+      do
       {
-        MenuResult<Integer> result = menu.run();
-        if (result.isSuccess())
+        promptAgain = false;
+        final MenuResult<Integer> result = menu.run();
+        throwIfMenuResultNotSucceeded(result);
+        userChoice = result.getValue();
+        if (TrustOption.CERTIFICATE_DETAILS.getChoice() == userChoice)
         {
-          if (result.getValue().equals(TrustOption.UNTRUSTED.getChoice()))
-          {
-            return false;
-          }
-
-          if (result.getValue().equals(
-              TrustOption.CERTIFICATE_DETAILS.getChoice()))
-          {
-            for (X509Certificate cert : chain)
-            {
-              app.println();
-              app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE.get(cert));
-            }
-            continue;
-          }
-
-          // We should add it in the memory truststore
-          for (X509Certificate cert : chain)
-          {
-            String alias = cert.getSubjectDN().getName();
-            try
-            {
-              state.truststore.setCertificateEntry(alias, cert);
-            }
-            catch (KeyStoreException e1)
-            {
-              // What else should we do?
-              return false;
-            }
-          }
-
-          // Update the trust manager
-          if (state.trustManager == null)
-          {
-            state.trustManager = new ApplicationTrustManager(state.truststore);
-          }
-          if (authType != null && host != null)
-          {
-            // Update the trust manager with the new certificate
-            state.trustManager.acceptCertificate(chain, authType, host);
-          }
-          else
-          {
-            // Do a full reset of the contents of the keystore.
-            state.trustManager = new ApplicationTrustManager(state.truststore);
-          }
-          if (result.getValue().equals(TrustOption.PERMAMENT.getChoice()))
-          {
-            ValidationCallback<String> callback =
-                new ValidationCallback<String>()
-                {
-                  @Override
-                  public String validate(ConsoleApplication app, String input)
-                      throws ClientException
-                  {
-                    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 (ClientException e)
-            {
-              return true;
-            }
-
-            // Read the password from the stdin.
-            String truststorePassword;
-            try
-            {
-              app.println();
-              LocalizableMessage prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD.get(truststorePath);
-              truststorePassword = 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 (X509Certificate cert : chain)
-              {
-                String alias = cert.getSubjectDN().getName();
-                ts.setCertificateEntry(alias, cert);
-              }
-              FileOutputStream fos = new FileOutputStream(truststorePath);
-              try
-              {
-                ts.store(fos, truststorePassword.toCharArray());
-              }
-              finally
-              {
-                fos.close();
-              }
-            }
-            catch (Exception e)
-            {
-              return true;
-            }
-          }
-          return true;
-        }
-        else
-        {
-          // Should never happen.
-          throw new RuntimeException();
+          promptAgain = true;
+          printCertificateDetails(chain);
         }
       }
-      catch (ClientException cliE)
+      while (promptAgain);
+
+      return trustCertificate(TrustOption.getTrustOptionForIndex(userChoice), chain, authType, host);
+    }
+    catch (ClientException e)
+    {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private void printCertificateChain(X509Certificate[] chain)
+  {
+    app.println();
+    app.println(INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE.get());
+    app.println();
+    boolean printSeparatorLines = false;
+    for (final X509Certificate cert : chain)
+    {
+      if (!printSeparatorLines)
       {
-        throw new RuntimeException(cliE);
+        app.println();
+        app.println();
+        printSeparatorLines = true;
+      }
+
+      // Certificate DN
+      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN.get(cert.getSubjectDN()));
+      // certificate validity
+      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY.get(
+          cert.getNotBefore(), cert.getNotAfter()));
+      // certificate Issuer
+      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER.get(cert.getIssuerDN()));
+    }
+  }
+
+  private void printCertificateDetails(X509Certificate[] chain)
+  {
+    for (X509Certificate cert : chain)
+    {
+      app.println();
+      app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE.get(cert));
+    }
+  }
+
+  private boolean trustCertificate(final TrustOption trustOption, final X509Certificate[] chain,
+      final String authType, final String host) throws ClientException
+  {
+    try
+    {
+      switch (trustOption)
+      {
+      case SESSION:
+        updateTrustManager(chain, authType, host);
+        return true;
+
+      case PERMAMENT:
+        updateTrustManager(chain, authType, host);
+        try
+        {
+          trustCertificatePermanently(chain);
+        }
+        catch (Exception e)
+        {
+          app.println(ERR_TRUSTING_CERTIFICATE_PERMANENTLY.get(e.getMessage()));
+        }
+        return true;
+
+      case UNTRUSTED:
+      default:
+        return false;
       }
     }
+    catch (KeyStoreException e)
+    {
+      app.println(ERR_TRUSTING_CERTIFICATE.get(e.getMessage()));
+      return false;
+    }
+  }
+
+  private void updateTrustManager(X509Certificate[] chain, String authType, String host) throws KeyStoreException
+  {
+    // User choice if to add the certificate to the trust store for the current session or permanently.
+    for (final X509Certificate cert : chain)
+    {
+      state.truststore.setCertificateEntry(cert.getSubjectDN().getName(), cert);
+    }
+
+    // Update the trust manager
+    if (state.trustManager == null)
+    {
+      state.trustManager = new ApplicationTrustManager(state.truststore);
+    }
+
+    if (authType != null && host != null)
+    {
+      // Update the trust manager with the new certificate
+      state.trustManager.acceptCertificate(chain, authType, host);
+    }
+    else
+    {
+      // Do a full reset of the contents of the keystore.
+      state.trustManager = new ApplicationTrustManager(state.truststore);
+    }
+  }
+
+  private void trustCertificatePermanently(final X509Certificate[] chain) throws Exception
+  {
+    app.println();
+    final String trustStorePath = app.readValidatedInput(
+        INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(),
+        filePathValidationCallback(!ALLOW_EMPTY_PATH, !FILE_MUST_EXISTS));
+
+    // Read the password from the stdin.
+    final String trustStorePasswordStr = readPassword(
+        INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD.get(trustStorePath));
+    final KeyStore keyStore = KeyStore.getInstance("JKS");
+    final char[] trustStorePassword = trustStorePasswordStr.toCharArray();
+    loadKeyStoreFromFile(keyStore, trustStorePath, trustStorePassword);
+
+    for (final X509Certificate cert : chain)
+    {
+      keyStore.setCertificateEntry(cert.getSubjectDN().getName(), cert);
+    }
+
+    try (final FileOutputStream trustStoreOutputFile = new FileOutputStream(trustStorePath))
+    {
+      keyStore.store(trustStoreOutputFile, trustStorePassword);
+    }
+  }
+
+  private void loadKeyStoreFromFile(
+      final KeyStore keyStore, final String trustStorePath, final char[] trustStorePassword) throws Exception
+  {
+      try (FileInputStream inputStream = new FileInputStream(trustStorePath))
+      {
+        keyStore.load(inputStream, trustStorePassword);
+      }
+      catch (FileNotFoundException ignored)
+      {
+        // create empty keystore
+        keyStore.load(null, trustStorePassword);
+      }
   }
 
   /**
@@ -1718,7 +1537,7 @@
     if (state.useSSL)
     {
       SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
-      sslConnectionFactory.init(getTrustManager() == null, state.keystorePath,
+      sslConnectionFactory.init(getTrustManager() == null, state.keyStorePath,
           state.keystorePassword, state.certifNickname, state.truststorePath, state.truststorePassword);
       options.setSSLConnectionFactory(sslConnectionFactory);
     }
@@ -1729,9 +1548,8 @@
   /**
    * Prompts the user to accept the certificate.
    *
-   * @param t
-   *          the throwable that was generated because the certificate was not
-   *          trusted.
+   * @param errorRaised
+   *          the error raised because the certificate was not trusted.
    * @param usedTrustManager
    *          the trustManager used when trying to establish the connection.
    * @param usedUrl
@@ -1741,85 +1559,58 @@
    * @return {@code true} if the user accepted the certificate and
    *         {@code false} otherwise.
    */
-  public boolean promptForCertificateConfirmation(Throwable t,
+  public boolean promptForCertificateConfirmation(Throwable errorRaised,
       ApplicationTrustManager usedTrustManager, String usedUrl, LocalizedLogger logger)
   {
-    ApplicationTrustManager.Cause cause;
-    if (usedTrustManager != null)
+    final ApplicationTrustManager.Cause cause = usedTrustManager != null ? usedTrustManager.getLastRefusedCause()
+                                                                         : null;
+    logger.debug(INFO_CERTIFICATE_EXCEPTION_CAUSE.get(cause));
+
+    if (cause == null)
     {
-      cause = usedTrustManager.getLastRefusedCause();
+      app.println(getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(), errorRaised));
+      return false;
+    }
+
+    String host;
+    int port;
+    try
+    {
+      URI uri = new URI(usedUrl);
+      host = uri.getHost();
+      port = uri.getPort();
+    }
+    catch (URISyntaxException e)
+    {
+      logger.warn(ERROR_CERTIFICATE_PARSING_URL.get(usedUrl, e));
+      host = INFO_NOT_AVAILABLE_LABEL.get().toString();
+      port = -1;
+    }
+
+    final String authType = usedTrustManager.getLastRefusedAuthType();
+    if (authType == null)
+    {
+      logger.warn(ERROR_CERTIFICATE_NULL_AUTH_TYPE.get());
     }
     else
     {
-      cause = null;
-    }
-    if (logger != null)
-    {
-      logger.debug(LocalizableMessage.raw("Certificate exception cause: " + cause));
+      app.println(ApplicationTrustManager.Cause.NOT_TRUSTED.equals(authType)
+          ? INFO_CERTIFICATE_NOT_TRUSTED_TEXT_CLI.get(host, port)
+          : INFO_CERTIFICATE_NAME_MISMATCH_TEXT_CLI.get(host, port, host, host, port));
     }
 
-    if (cause != null)
+    final X509Certificate[] chain = usedTrustManager.getLastRefusedChain();
+    if (chain == null)
     {
-      String h;
-      int p;
-      try
-      {
-        URI uri = new URI(usedUrl);
-        h = uri.getHost();
-        p = uri.getPort();
-      }
-      catch (Throwable t1)
-      {
-        printLogger(logger, "Error parsing ldap url of ldap url. " + t1);
-        h = INFO_NOT_AVAILABLE_LABEL.get().toString();
-        p = -1;
-      }
-
-      String authType = usedTrustManager.getLastRefusedAuthType();
-      if (authType == null)
-      {
-        printLogger(logger, "Null auth type for this certificate exception.");
-      }
-      else
-      {
-        LocalizableMessage msg;
-        if (authType.equals(ApplicationTrustManager.Cause.NOT_TRUSTED))
-        {
-          msg = INFO_CERTIFICATE_NOT_TRUSTED_TEXT_CLI.get(h, p);
-        }
-        else
-        {
-          msg = INFO_CERTIFICATE_NAME_MISMATCH_TEXT_CLI.get(h, p, h, h, p);
-        }
-        app.println(msg);
-      }
-
-      X509Certificate[] chain = usedTrustManager.getLastRefusedChain();
-      if (chain == null)
-      {
-        printLogger(logger, "Null chain for this certificate exception.");
-        return false;
-      }
-      if (h == null)
-      {
-        printLogger(logger, "Null host name for this certificate exception.");
-      }
-      return checkServerCertificate(chain, authType, h);
+      logger.warn(ERROR_CERTIFICATE_NULL_CHAIN.get());
+      return false;
     }
-    else
+    if (host == null)
     {
-      app.println(getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(), t));
+      logger.warn(ERROR_CERTIFICATE_NULL_HOST_NAME.get());
     }
-    return false;
-  }
 
-  private void printLogger(final LocalizedLogger logger,
-      final String msg)
-  {
-    if (logger != null)
-    {
-      logger.warn(LocalizableMessage.raw(msg));
-    }
+    return checkServerCertificate(chain, authType, host);
   }
 
   /**
@@ -1860,20 +1651,9 @@
   }
 
   /**
-   * Tells whether during interaction we can ask for both the DN or the admin
-   * UID.
-   *
-   * @return {@code true} if during interaction we can ask for both the DN
-   *         and the admin UID and {@code false} otherwise.
-   */
-  public boolean isUseAdminOrBindDn()
-  {
-    return useAdminOrBindDn;
-  }
-
-  /**
    * Tells whether we can ask during interaction for both the DN and the admin
    * UID or not.
+   * Default value is {@code false}.
    *
    * @param useAdminOrBindDn
    *          whether we can ask for both the DN and the admin UID during
@@ -1893,8 +1673,7 @@
    *          whether propose LDAP as protocol even if the user provided
    *          security parameters or not.
    */
-  public void setDisplayLdapIfSecureParameters(
-      boolean displayLdapIfSecureParameters)
+  public void setDisplayLdapIfSecureParameters(boolean displayLdapIfSecureParameters)
   {
     this.displayLdapIfSecureParameters = displayLdapIfSecureParameters;
   }
@@ -1919,7 +1698,7 @@
   {
     if (!state.trustManagerInitialized)
     {
-      initializeTrustManager();
+      initializeTrustAndKeyManagers();
     }
   }
 
@@ -1948,46 +1727,46 @@
     resetConnectionArguments();
     if (hostName != null)
     {
-      secureArgsList.hostNameArg.addValue(hostName);
-      secureArgsList.hostNameArg.setPresent(true);
+      secureArgsList.getHostNameArg().addValue(hostName);
+      secureArgsList.getHostNameArg().setPresent(true);
     }
     // resetConnectionArguments does not clear the values for the port
-    secureArgsList.portArg.clearValues();
+    secureArgsList.getPortArg().clearValues();
     if (port != -1)
     {
-      secureArgsList.portArg.addValue(String.valueOf(port));
-      secureArgsList.portArg.setPresent(true);
+      secureArgsList.getPortArg().addValue(String.valueOf(port));
+      secureArgsList.getPortArg().setPresent(true);
     }
     else
     {
       // This is done to be able to call IntegerArgument.getIntValue()
-      secureArgsList.portArg.addValue(secureArgsList.portArg.getDefaultValue());
+      secureArgsList.getPortArg().addValue(secureArgsList.getPortArg().getDefaultValue());
     }
-    secureArgsList.useSSLArg.setPresent(state.useSSL);
-    secureArgsList.useStartTLSArg.setPresent(state.useStartTLS);
+    secureArgsList.getUseSSLArg().setPresent(state.useSSL);
+    secureArgsList.getUseStartTLSArg().setPresent(state.useStartTLS);
     if (adminUid != null)
     {
-      secureArgsList.adminUidArg.addValue(adminUid);
-      secureArgsList.adminUidArg.setPresent(true);
+      secureArgsList.getAdminUidArg().addValue(adminUid);
+      secureArgsList.getAdminUidArg().setPresent(true);
     }
     if (bindDn != null)
     {
-      secureArgsList.bindDnArg.addValue(bindDn);
-      secureArgsList.bindDnArg.setPresent(true);
+      secureArgsList.getBindDnArg().addValue(bindDn);
+      secureArgsList.getBindDnArg().setPresent(true);
     }
     if (pwdFile != null)
     {
-      secureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(pwdFile);
+      secureArgsList.getBindPasswordFileArg().getNameToValueMap().putAll(pwdFile);
       for (String value : pwdFile.keySet())
       {
-        secureArgsList.bindPasswordFileArg.addValue(value);
+        secureArgsList.getBindPasswordFileArg().addValue(value);
       }
-      secureArgsList.bindPasswordFileArg.setPresent(true);
+      secureArgsList.getBindPasswordFileArg().setPresent(true);
     }
     else if (bindPwd != null)
     {
-      secureArgsList.bindPasswordArg.addValue(bindPwd);
-      secureArgsList.bindPasswordArg.setPresent(true);
+      secureArgsList.getBindPasswordArg().addValue(bindPwd);
+      secureArgsList.getBindPasswordArg().setPresent(true);
     }
     state = new State(secureArgsList);
   }
@@ -2000,32 +1779,30 @@
    */
   public void resetConnectionArguments()
   {
-    secureArgsList.hostNameArg.clearValues();
-    secureArgsList.hostNameArg.setPresent(false);
-    secureArgsList.portArg.clearValues();
-    secureArgsList.portArg.setPresent(false);
+    secureArgsList.getHostNameArg().clearValues();
+    secureArgsList.getHostNameArg().setPresent(false);
+    secureArgsList.getPortArg().clearValues();
+    secureArgsList.getPortArg().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.getPortArg().addValue(secureArgsList.getPortArg().getDefaultValue());
+    secureArgsList.getBindDnArg().clearValues();
+    secureArgsList.getBindDnArg().setPresent(false);
+    secureArgsList.getBindPasswordArg().clearValues();
+    secureArgsList.getBindPasswordArg().setPresent(false);
+    secureArgsList.getBindPasswordFileArg().clearValues();
+    secureArgsList.getBindPasswordFileArg().getNameToValueMap().clear();
+    secureArgsList.getBindPasswordFileArg().setPresent(false);
     state.bindPassword = null;
-    secureArgsList.adminUidArg.clearValues();
-    secureArgsList.adminUidArg.setPresent(false);
+    secureArgsList.getAdminUidArg().clearValues();
+    secureArgsList.getAdminUidArg().setPresent(false);
   }
 
-  private void initializeTrustManager() throws ArgumentException
+  private void initializeTrustAndKeyManagers() throws ArgumentException
   {
     // Get trust store info
     state.trustManager = getTrustManagerInternal();
-
     // Check if we need client side authentication
     state.keyManager = getKeyManagerInternal();
-
     state.trustManagerInitialized = true;
   }
 
@@ -2058,35 +1835,33 @@
    *
    * @return true if the local trust store has been added.
    */
-  private boolean addLocalTrustStore()
+  private boolean useLocalTrustStoreIfPossible()
   {
     try
     {
-      // If remote host, return
-      if (!InetAddress.getLocalHost().getHostName().equals(state.hostName)
-          || secureArgsList.getAdminPortFromConfig() != portNumber)
+      if (InetAddress.getLocalHost().getHostName().equals(state.hostName)
+          && secureArgsList.getAdminPortFromConfig() == portNumber)
       {
-        return false;
+        final String trustStoreFileAbsolute = secureArgsList.getTruststoreFileFromConfig();
+        if (trustStoreFileAbsolute != null)
+        {
+          secureArgsList.getTrustStorePathArg().addValue(trustStoreFileAbsolute);
+          return true;
+        }
       }
-      // 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;
-      }
-      return false;
     }
     catch (Exception ex)
     {
       // do nothing
-      return false;
+    }
+    return false;
+  }
+
+  private void throwIfMenuResultNotSucceeded(final MenuResult<?> result)
+  {
+    if (!result.isSuccess())
+    {
+      throw new RuntimeException("Expected successful menu result, but got " + result);
     }
   }
 }

--
Gitblit v1.10.0