/**
  * Creates a connection using a console interaction that will be used to potentially interact with
  * the user to prompt for necessary information for establishing the connection.
  *
  * @param ui user interaction for prompting the user
  * @param out stream to write messages
  * @param err stream to write error messages
  * @return LDAPConnection created by this class from parsed arguments
  * @throws LDAPConnectionException if there was a problem connecting to the server indicated by
  *     the input arguments
  */
 public LDAPConnection connect(
     LDAPConnectionConsoleInteraction ui, PrintStream out, PrintStream err)
     throws LDAPConnectionException {
   try {
     ui.run();
     LDAPConnectionOptions options = new LDAPConnectionOptions();
     options.setVersionNumber(3);
     return connect(
         ui.getHostName(),
         ui.getPortNumber(),
         ui.getBindDN(),
         ui.getBindPassword(),
         ui.populateLDAPOptions(options),
         ui.getConnectTimeout(),
         out,
         err);
   } catch (ArgumentException | OpenDsException e) {
     if (e.getCause() != null
         && e.getCause().getCause() != null
         && e.getCause().getCause() instanceof SSLException) {
       err.println(ERR_TASKINFO_LDAP_EXCEPTION_SSL.get(ui.getHostName(), ui.getPortNumber()));
     } else {
       err.println(e.getMessageObject());
     }
   }
   return null;
 }
  /**
   * Creates a new LDAPConnection and invokes a connect operation using information provided in the
   * parsed set of arguments that were provided by the user.
   *
   * @param args with which to connect
   * @param out stream to write messages
   * @param err stream to write error messages
   * @return LDAPConnection created by this class from parsed arguments
   * @throws LDAPConnectionException if there was a problem connecting to the server indicated by
   *     the input arguments
   * @throws ArgumentException if there was a problem processing the input arguments
   */
  private LDAPConnection connect(SecureConnectionCliArgs args, PrintStream out, PrintStream err)
      throws LDAPConnectionException, ArgumentException {
    // If both a bind password and bind password file were provided, then return
    // an error.
    if (args.bindPasswordArg.isPresent() && args.bindPasswordFileArg.isPresent()) {
      printAndThrowException(
          err,
          ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
              args.bindPasswordArg.getLongIdentifier(),
              args.bindPasswordFileArg.getLongIdentifier()));
    }

    // If both a key store password and key store password file were provided,
    // then return an error.
    if (args.keyStorePasswordArg.isPresent() && args.keyStorePasswordFileArg.isPresent()) {
      printAndThrowException(
          err,
          ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
              args.keyStorePasswordArg.getLongIdentifier(),
              args.keyStorePasswordFileArg.getLongIdentifier()));
    }

    // If both a trust store password and trust store password file were
    // provided, then return an error.
    if (args.trustStorePasswordArg.isPresent() && args.trustStorePasswordFileArg.isPresent()) {
      printAndThrowException(
          err,
          ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
              args.trustStorePasswordArg.getLongIdentifier(),
              args.trustStorePasswordFileArg.getLongIdentifier()));
    }

    // Create the LDAP connection options object, which will be used to
    // customize the way that we connect to the server and specify a set of
    // basic defaults.
    LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
    connectionOptions.setVersionNumber(3);

    // See if we should use SSL or StartTLS when establishing the connection.
    // If so, then make sure only one of them was specified.
    if (args.useSSLArg.isPresent()) {
      if (args.useStartTLSArg.isPresent()) {
        printAndThrowException(
            err,
            ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
                args.useSSLArg.getLongIdentifier(), args.useSSLArg.getLongIdentifier()));
      }
      connectionOptions.setUseSSL(true);
    } else if (args.useStartTLSArg.isPresent()) {
      connectionOptions.setStartTLS(true);
    }

    // If we should blindly trust any certificate, then install the appropriate
    // SSL connection factory.
    if (args.useSSLArg.isPresent() || args.useStartTLSArg.isPresent()) {
      try {
        String clientAlias;
        if (args.certNicknameArg.isPresent()) {
          clientAlias = args.certNicknameArg.getValue();
        } else {
          clientAlias = null;
        }

        SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
        sslConnectionFactory.init(
            args.trustAllArg.isPresent(),
            args.keyStorePathArg.getValue(),
            args.keyStorePasswordArg.getValue(),
            clientAlias,
            args.trustStorePathArg.getValue(),
            args.trustStorePasswordArg.getValue());
        connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
      } catch (SSLConnectionException sce) {
        printWrappedText(err, ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL.get(sce.getMessage()));
      }
    }

    // If one or more SASL options were provided, then make sure that one of
    // them was "mech" and specified a valid SASL mechanism.
    if (args.saslOptionArg.isPresent()) {
      String mechanism = null;
      LinkedList<String> options = new LinkedList<>();

      for (String s : args.saslOptionArg.getValues()) {
        int equalPos = s.indexOf('=');
        if (equalPos <= 0) {
          printAndThrowException(err, ERR_LDAP_CONN_CANNOT_PARSE_SASL_OPTION.get(s));
        } else {
          String name = s.substring(0, equalPos);
          if ("mech".equalsIgnoreCase(name)) {
            mechanism = s;
          } else {
            options.add(s);
          }
        }
      }

      if (mechanism == null) {
        printAndThrowException(err, ERR_LDAP_CONN_NO_SASL_MECHANISM.get());
      }

      connectionOptions.setSASLMechanism(mechanism);
      for (String option : options) {
        connectionOptions.addSASLProperty(option);
      }
    }

    int timeout = args.connectTimeoutArg.getIntValue();

    final String passwordValue =
        getPasswordValue(args.bindPasswordArg, args.bindPasswordFileArg, args.bindDnArg, out, err);
    return connect(
        args.hostNameArg.getValue(),
        args.portArg.getIntValue(),
        args.bindDnArg.getValue(),
        passwordValue,
        connectionOptions,
        timeout,
        out,
        err);
  }