mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
25.33.2012 263d085885df024dca9250cc03c807912b0a7662
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -26,8 +25,6 @@
 */
package com.forgerock.opendj.ldap.tools;
import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
import static com.forgerock.opendj.util.StaticUtils.EOL;
@@ -37,721 +34,573 @@
import java.util.StringTokenizer;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.controls.*;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl;
import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl;
import org.forgerock.opendj.ldap.controls.GenericControl;
import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl;
import org.forgerock.opendj.ldap.controls.PasswordExpiredResponseControl;
import org.forgerock.opendj.ldap.controls.PasswordExpiringResponseControl;
import org.forgerock.opendj.ldap.controls.PasswordPolicyErrorType;
import org.forgerock.opendj.ldap.controls.PasswordPolicyRequestControl;
import org.forgerock.opendj.ldap.controls.PasswordPolicyResponseControl;
import org.forgerock.opendj.ldap.controls.PasswordPolicyWarningType;
import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
import org.forgerock.opendj.ldap.responses.BindResult;
import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl;
import com.forgerock.opendj.ldap.tools.AuthenticatedConnectionFactory.*;
import com.forgerock.opendj.ldap.tools.AuthenticatedConnectionFactory.AuthenticatedConnection;
import com.forgerock.opendj.util.StaticUtils;
/**
 * This class provides utility functions for all the client side tools.
 */
final class Utils
{
  /**
   * The name of a command-line script used to launch a tool.
   */
  static final String PROPERTY_SCRIPT_NAME = "com.forgerock.opendj.ldap.tools.scriptName";
final class Utils {
    /**
     * The name of a command-line script used to launch a tool.
     */
    static final String PROPERTY_SCRIPT_NAME = "com.forgerock.opendj.ldap.tools.scriptName";
  /**
   * The column at which to wrap long lines of output in the command-line tools.
   */
  static final int MAX_LINE_WIDTH;
    /**
     * The column at which to wrap long lines of output in the command-line
     * tools.
     */
    static final int MAX_LINE_WIDTH;
  static
  {
    int columns = 80;
    try
    {
      final String s = System.getenv("COLUMNS");
      if (s != null)
      {
        columns = Integer.parseInt(s);
      }
    }
    catch (final Exception e)
    {
      // Do nothing.
    }
    MAX_LINE_WIDTH = columns - 1;
  }
  /**
   * Filters the provided value to ensure that it is appropriate for use as an
   * exit code. Exit code values are generally only allowed to be between 0 and
   * 255, so any value outside of this range will be converted to 255, which is
   * the typical exit code used to indicate an overflow value.
   *
   * @param exitCode
   *          The exit code value to be processed.
   * @return An integer value between 0 and 255, inclusive. If the provided exit
   *         code was already between 0 and 255, then the original value will be
   *         returned. If the provided value was out of this range, then 255
   *         will be returned.
   */
  static int filterExitCode(final int exitCode)
  {
    if (exitCode < 0)
    {
      return 255;
    }
    else if (exitCode > 255)
    {
      return 255;
    }
    else
    {
      return exitCode;
    }
  }
  /**
   * Parse the specified command line argument to create the appropriate
   * LDAPControl. The argument string should be in the format
   * controloid[:criticality[:value|::b64value|:&lt;fileurl]]
   *
   * @param argString
   *          The argument string containing the encoded control information.
   * @return The control decoded from the provided string, or <CODE>null</CODE>
   *         if an error occurs while parsing the argument value.
   * @throws org.forgerock.opendj.ldap.DecodeException
   *           If an error occurs.
   */
  static GenericControl getControl(final String argString)
      throws DecodeException
  {
    String controlOID = null;
    boolean controlCriticality = false;
    ByteString controlValue = null;
    int idx = argString.indexOf(":");
    if (idx < 0)
    {
      controlOID = argString;
    }
    else
    {
      controlOID = argString.substring(0, idx);
    }
    final String lowerOID = StaticUtils.toLowerCase(controlOID);
    if (lowerOID.equals("accountusable") || lowerOID.equals("accountusability"))
    {
      controlOID = AccountUsabilityRequestControl.OID;
    }
    else if (lowerOID.equals("authzid")
        || lowerOID.equals("authorizationidentity"))
    {
      controlOID = AuthorizationIdentityRequestControl.OID;
    }
    else if (lowerOID.equals("noop") || lowerOID.equals("no-op"))
    {
      // controlOID = OID_LDAP_NOOP_OPENLDAP_ASSIGNED;
    }
    else if (lowerOID.equals("subentries"))
    {
      // controlOID = OID_LDAP_SUBENTRIES;
    }
    else if (lowerOID.equals("managedsait"))
    {
      // controlOID = OID_MANAGE_DSAIT_CONTROL;
    }
    else if (lowerOID.equals("pwpolicy") || lowerOID.equals("passwordpolicy"))
    {
      controlOID = PasswordPolicyRequestControl.OID;
    }
    else if (lowerOID.equals("subtreedelete") || lowerOID.equals("treedelete"))
    {
      controlOID = SubtreeDeleteRequestControl.OID;
    }
    else if (lowerOID.equals("realattrsonly")
        || lowerOID.equals("realattributesonly"))
    {
      // controlOID = OID_REAL_ATTRS_ONLY;
    }
    else if (lowerOID.equals("virtualattrsonly")
        || lowerOID.equals("virtualattributesonly"))
    {
      // controlOID = OID_VIRTUAL_ATTRS_ONLY;
    }
    else if (lowerOID.equals("effectiverights")
        || lowerOID.equals("geteffectiverights"))
    {
      controlOID = GetEffectiveRightsRequestControl.OID;
    }
    if (idx < 0)
    {
      return GenericControl.newControl(controlOID);
    }
    final String remainder = argString.substring(idx + 1, argString.length());
    idx = remainder.indexOf(":");
    if (idx == -1)
    {
      if (remainder.equalsIgnoreCase("true"))
      {
        controlCriticality = true;
      }
      else if (remainder.equalsIgnoreCase("false"))
      {
        controlCriticality = false;
      }
      else
      {
        // TODO: I18N
        throw DecodeException.error(LocalizableMessage
            .raw("Invalid format for criticality value:" + remainder));
      }
      return GenericControl.newControl(controlOID, controlCriticality);
    }
    final String critical = remainder.substring(0, idx);
    if (critical.equalsIgnoreCase("true"))
    {
      controlCriticality = true;
    }
    else if (critical.equalsIgnoreCase("false"))
    {
      controlCriticality = false;
    }
    else
    {
      // TODO: I18N
      throw DecodeException.error(LocalizableMessage
          .raw("Invalid format for criticality value:" + critical));
    }
    final String valString = remainder.substring(idx + 1, remainder.length());
    if (valString.charAt(0) == ':')
    {
      controlValue = ByteString.valueOf(valString.substring(1, valString
          .length()));
    }
    else if (valString.charAt(0) == '<')
    {
      // Read data from the file.
      final String filePath = valString.substring(1, valString.length());
      try
      {
        final byte[] val = readBytesFromFile(filePath);
        controlValue = ByteString.wrap(val);
      }
      catch (final Exception e)
      {
        return null;
      }
    }
    else
    {
      controlValue = ByteString.valueOf(valString);
    }
    return GenericControl.newControl(controlOID, controlCriticality,
        controlValue);
  }
  /**
   * Prints a multi-line error message with the provided information to the
   * given print stream.
   *
   * @param app
   *          The console app to use to write the error message.
   * @param ere
   *          The error result.
   * @return The error code.
   */
  static int printErrorMessage(final ConsoleApplication app,
      final ErrorResultException ere)
  {
    // if ((ere.getMessage() != null) && (ere.getMessage().length() >
    // 0))
    // {
    // app.println(LocalizableMessage.raw(ere.getMessage()));
    // }
    if (ere.getResult().getResultCode().intValue() >= 0)
    {
      app.println(ERR_TOOL_RESULT_CODE.get(ere.getResult().getResultCode()
          .intValue(), ere.getResult().getResultCode().toString()));
    }
    if ((ere.getResult().getDiagnosticMessage() != null)
        && (ere.getResult().getDiagnosticMessage().length() > 0))
    {
      app.println(ERR_TOOL_ERROR_MESSAGE.get(ere.getResult()
          .getDiagnosticMessage()));
    }
    if (ere.getResult().getMatchedDN() != null
        && ere.getResult().getMatchedDN().length() > 0)
    {
      app.println(ERR_TOOL_MATCHED_DN.get(ere.getResult().getMatchedDN()));
    }
    if (app.isVerbose() && ere.getResult().getCause() != null)
    {
      ere.getResult().getCause().printStackTrace(app.getErrorStream());
    }
    return ere.getResult().getResultCode().intValue();
  }
  static void printPasswordPolicyResults(final ConsoleApplication app,
      final Connection connection)
  {
    if (connection instanceof AuthenticatedConnection)
    {
      final AuthenticatedConnection conn = (AuthenticatedConnection) connection;
      final BindResult result = conn.getAuthenticatedBindResult();
      try
      {
        final AuthorizationIdentityResponseControl control = result.getControl(
            AuthorizationIdentityResponseControl.DECODER, new DecodeOptions());
        if (control != null)
        {
          final LocalizableMessage message = INFO_BIND_AUTHZID_RETURNED
              .get(control.getAuthorizationID());
          app.println(message);
        }
      }
      catch (final DecodeException e)
      {
        app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
      }
      try
      {
        final PasswordExpiredResponseControl control = result.getControl(
            PasswordExpiredResponseControl.DECODER, new DecodeOptions());
        if (control != null)
        {
          final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get();
          app.println(message);
        }
      }
      catch (final DecodeException e)
      {
        app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
      }
      try
      {
        final PasswordExpiringResponseControl control = result.getControl(
            PasswordExpiringResponseControl.DECODER, new DecodeOptions());
        if (control != null)
        {
          final LocalizableMessage timeString = Utils
              .secondsToTimeString(control.getSecondsUntilExpiration());
          final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING
              .get(timeString);
          app.println(message);
        }
      }
      catch (final DecodeException e)
      {
        app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
      }
      try
      {
        final PasswordPolicyResponseControl control = result.getControl(
            PasswordPolicyResponseControl.DECODER, new DecodeOptions());
        if (control != null)
        {
          final PasswordPolicyErrorType errorType = control.getErrorType();
          if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED)
          {
            final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get();
            app.println(message);
          }
          else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED)
          {
            final LocalizableMessage message = INFO_BIND_ACCOUNT_LOCKED.get();
            app.println(message);
          }
          else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET)
          {
            final LocalizableMessage message = INFO_BIND_MUST_CHANGE_PASSWORD
                .get();
            app.println(message);
          }
          final PasswordPolicyWarningType warningType = control
              .getWarningType();
          if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION)
          {
            final LocalizableMessage timeString = Utils
                .secondsToTimeString(control.getWarningValue());
            final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING
                .get(timeString);
            app.println(message);
          }
          else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING)
          {
            final LocalizableMessage message = INFO_BIND_GRACE_LOGINS_REMAINING
                .get(control.getWarningValue());
            app.println(message);
          }
        }
      }
      catch (final DecodeException e)
      {
        app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
      }
    }
  }
  /**
   * Read the data from the specified file and return it in a byte array.
   *
   * @param filePath
   *          The path to the file that should be read.
   * @return A byte array containing the contents of the requested file.
   * @throws IOException
   *           If a problem occurs while trying to read the specified file.
   */
  static byte[] readBytesFromFile(final String filePath) throws IOException
  {
    byte[] val = null;
    FileInputStream fis = null;
    try
    {
      final File file = new File(filePath);
      fis = new FileInputStream(file);
      final long length = file.length();
      val = new byte[(int) length];
      // Read in the bytes
      int offset = 0;
      int numRead = 0;
      while (offset < val.length
          && (numRead = fis.read(val, offset, val.length - offset)) >= 0)
      {
        offset += numRead;
      }
      // Ensure all the bytes have been read in
      if (offset < val.length)
      {
        throw new IOException("Could not completely read file " + filePath);
      }
      return val;
    }
    finally
    {
      if (fis != null)
      {
        fis.close();
      }
    }
  }
  /**
   * Retrieves a user-friendly string that indicates the length of time (in
   * days, hours, minutes, and seconds) in the specified number of seconds.
   *
   * @param numSeconds
   *          The number of seconds to be converted to a more user-friendly
   *          value.
   * @return The user-friendly representation of the specified number of
   *         seconds.
   */
  static LocalizableMessage secondsToTimeString(final int numSeconds)
  {
    if (numSeconds < 60)
    {
      // We can express it in seconds.
      return INFO_TIME_IN_SECONDS.get(numSeconds);
    }
    else if (numSeconds < 3600)
    {
      // We can express it in minutes and seconds.
      final int m = numSeconds / 60;
      final int s = numSeconds % 60;
      return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
    }
    else if (numSeconds < 86400)
    {
      // We can express it in hours, minutes, and seconds.
      final int h = numSeconds / 3600;
      final int m = (numSeconds % 3600) / 60;
      final int s = numSeconds % 3600 % 60;
      return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
    }
    else
    {
      // We can express it in days, hours, minutes, and seconds.
      final int d = numSeconds / 86400;
      final int h = (numSeconds % 86400) / 3600;
      final int m = (numSeconds % 86400 % 3600) / 60;
      final int s = numSeconds % 86400 % 3600 % 60;
      return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
    }
  }
  /**
   * Sets default system property settings for the xxxrate performance tools.
   */
  static void setDefaultPerfToolProperties()
  {
    // Use SameThreadStrategy by default.
    if (System
        .getProperty("org.forgerock.opendj.transport.useWorkerThreads") == null)
    {
      System.setProperty(
          "org.forgerock.opendj.transport.useWorkerThreads", "false");
    }
    // Configure connections to be terminate immediately after closing (this
    // prevents port exhaustion in xxxrate tools when connecting/disconnecting).
    if (System.getProperty("org.forgerock.opendj.transport.linger") == null)
    {
      System.setProperty("org.forgerock.opendj.transport.linger", "0");
    }
  }
  /**
   * Inserts line breaks into the provided buffer to wrap text at no more than
   * the specified column width. Wrapping will only be done at space boundaries
   * and if there are no spaces within the specified width, then wrapping will
   * be performed at the first space after the specified column.
   *
   * @param message
   *          The message to be wrapped.
   * @param width
   *          The maximum number of characters to allow on a line if there is a
   *          suitable breaking point.
   * @return The wrapped text.
   */
  static String wrapText(final LocalizableMessage message, final int width)
  {
    return wrapText(message.toString(), width, 0);
  }
  /**
   * Inserts line breaks into the provided buffer to wrap text at no more than
   * the specified column width. Wrapping will only be done at space boundaries
   * and if there are no spaces within the specified width, then wrapping will
   * be performed at the first space after the specified column. In addition
   * each line will be indented by the specified amount.
   *
   * @param message
   *          The message to be wrapped.
   * @param width
   *          The maximum number of characters to allow on a line if there is a
   *          suitable breaking point (including any indentation).
   * @param indent
   *          The number of columns to indent each line.
   * @return The wrapped text.
   */
  static String wrapText(final LocalizableMessage message, final int width,
      final int indent)
  {
    return wrapText(message.toString(), width, indent);
  }
  /**
   * Inserts line breaks into the provided buffer to wrap text at no more than
   * the specified column width. Wrapping will only be done at space boundaries
   * and if there are no spaces within the specified width, then wrapping will
   * be performed at the first space after the specified column.
   *
   * @param text
   *          The text to be wrapped.
   * @param width
   *          The maximum number of characters to allow on a line if there is a
   *          suitable breaking point.
   * @return The wrapped text.
   */
  static String wrapText(final String text, final int width)
  {
    return wrapText(text, width, 0);
  }
  /**
   * Inserts line breaks into the provided buffer to wrap text at no more than
   * the specified column width. Wrapping will only be done at space boundaries
   * and if there are no spaces within the specified width, then wrapping will
   * be performed at the first space after the specified column. In addition
   * each line will be indented by the specified amount.
   *
   * @param text
   *          The text to be wrapped.
   * @param width
   *          The maximum number of characters to allow on a line if there is a
   *          suitable breaking point (including any indentation).
   * @param indent
   *          The number of columns to indent each line.
   * @return The wrapped text.
   */
  static String wrapText(final String text, int width, final int indent)
  {
    // Calculate the real width and indentation padding.
    width -= indent;
    final StringBuilder pb = new StringBuilder();
    for (int i = 0; i < indent; i++)
    {
      pb.append(' ');
    }
    final String padding = pb.toString();
    final StringBuilder buffer = new StringBuilder();
    if (text != null)
    {
      final StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n",
          true);
      while (lineTokenizer.hasMoreTokens())
      {
        final String line = lineTokenizer.nextToken();
        if (line.equals("\r") || line.equals("\n"))
        {
          // It's an end-of-line character, so append it as-is.
          buffer.append(line);
        }
        else if (line.length() <= width)
        {
          // The line fits in the specified width, so append it as-is.
          buffer.append(padding);
          buffer.append(line);
        }
        else
        {
          // The line doesn't fit in the specified width, so it needs to
          // be
          // wrapped. Do so at space boundaries.
          StringBuilder lineBuffer = new StringBuilder();
          StringBuilder delimBuffer = new StringBuilder();
          final StringTokenizer wordTokenizer = new StringTokenizer(line, " ",
              true);
          while (wordTokenizer.hasMoreTokens())
          {
            final String word = wordTokenizer.nextToken();
            if (word.equals(" "))
            {
              // It's a space, so add it to the delim buffer only if the
              // line
              // buffer is not empty.
              if (lineBuffer.length() > 0)
              {
                delimBuffer.append(word);
              }
    static {
        int columns = 80;
        try {
            final String s = System.getenv("COLUMNS");
            if (s != null) {
                columns = Integer.parseInt(s);
            }
            else if (word.length() > width)
            {
              // This is a long word that can't be wrapped, so we'll
              // just have
              // to make do.
              if (lineBuffer.length() > 0)
              {
                buffer.append(padding);
                buffer.append(lineBuffer);
                buffer.append(EOL);
                lineBuffer = new StringBuilder();
              }
              buffer.append(padding);
              buffer.append(word);
              if (wordTokenizer.hasMoreTokens())
              {
                // The next token must be a space, so remove it. If
                // there are
                // still more tokens after that, then append an EOL.
                wordTokenizer.nextToken();
                if (wordTokenizer.hasMoreTokens())
                {
                  buffer.append(EOL);
                }
              }
              if (delimBuffer.length() > 0)
              {
                delimBuffer = new StringBuilder();
              }
            }
            else
            {
              // It's not a space, so see if we can fit it on the curent
              // line.
              final int newLineLength = lineBuffer.length()
                  + delimBuffer.length() + word.length();
              if (newLineLength < width)
              {
                // It does fit on the line, so add it.
                lineBuffer.append(delimBuffer).append(word);
                if (delimBuffer.length() > 0)
                {
                  delimBuffer = new StringBuilder();
                }
              }
              else
              {
                // It doesn't fit on the line, so end the current line
                // and start
                // a new one.
                buffer.append(padding);
                buffer.append(lineBuffer);
                buffer.append(EOL);
                lineBuffer = new StringBuilder();
                lineBuffer.append(word);
                if (delimBuffer.length() > 0)
                {
                  delimBuffer = new StringBuilder();
                }
              }
            }
          }
          // If there's anything left in the line buffer, then add it to
          // the
          // final buffer.
          buffer.append(padding);
          buffer.append(lineBuffer);
        } catch (final Exception e) {
            // Do nothing.
        }
      }
        MAX_LINE_WIDTH = columns - 1;
    }
    return buffer.toString();
  }
    /**
     * Filters the provided value to ensure that it is appropriate for use as an
     * exit code. Exit code values are generally only allowed to be between 0
     * and 255, so any value outside of this range will be converted to 255,
     * which is the typical exit code used to indicate an overflow value.
     *
     * @param exitCode
     *            The exit code value to be processed.
     * @return An integer value between 0 and 255, inclusive. If the provided
     *         exit code was already between 0 and 255, then the original value
     *         will be returned. If the provided value was out of this range,
     *         then 255 will be returned.
     */
    static int filterExitCode(final int exitCode) {
        if (exitCode < 0) {
            return 255;
        } else if (exitCode > 255) {
            return 255;
        } else {
            return exitCode;
        }
    }
    /**
     * Parse the specified command line argument to create the appropriate
     * LDAPControl. The argument string should be in the format
     * controloid[:criticality[:value|::b64value|:&lt;fileurl]]
     *
     * @param argString
     *            The argument string containing the encoded control
     *            information.
     * @return The control decoded from the provided string, or
     *         <CODE>null</CODE> if an error occurs while parsing the argument
     *         value.
     * @throws org.forgerock.opendj.ldap.DecodeException
     *             If an error occurs.
     */
    static GenericControl getControl(final String argString) throws DecodeException {
        String controlOID = null;
        boolean controlCriticality = false;
        ByteString controlValue = null;
  // Prevent instantiation.
  private Utils()
  {
    // Do nothing.
  }
        int idx = argString.indexOf(":");
        if (idx < 0) {
            controlOID = argString;
        } else {
            controlOID = argString.substring(0, idx);
        }
        final String lowerOID = StaticUtils.toLowerCase(controlOID);
        if (lowerOID.equals("accountusable") || lowerOID.equals("accountusability")) {
            controlOID = AccountUsabilityRequestControl.OID;
        } else if (lowerOID.equals("authzid") || lowerOID.equals("authorizationidentity")) {
            controlOID = AuthorizationIdentityRequestControl.OID;
        } else if (lowerOID.equals("noop") || lowerOID.equals("no-op")) {
            // controlOID = OID_LDAP_NOOP_OPENLDAP_ASSIGNED;
        } else if (lowerOID.equals("subentries")) {
            // controlOID = OID_LDAP_SUBENTRIES;
        } else if (lowerOID.equals("managedsait")) {
            // controlOID = OID_MANAGE_DSAIT_CONTROL;
        } else if (lowerOID.equals("pwpolicy") || lowerOID.equals("passwordpolicy")) {
            controlOID = PasswordPolicyRequestControl.OID;
        } else if (lowerOID.equals("subtreedelete") || lowerOID.equals("treedelete")) {
            controlOID = SubtreeDeleteRequestControl.OID;
        } else if (lowerOID.equals("realattrsonly") || lowerOID.equals("realattributesonly")) {
            // controlOID = OID_REAL_ATTRS_ONLY;
        } else if (lowerOID.equals("virtualattrsonly") || lowerOID.equals("virtualattributesonly")) {
            // controlOID = OID_VIRTUAL_ATTRS_ONLY;
        } else if (lowerOID.equals("effectiverights") || lowerOID.equals("geteffectiverights")) {
            controlOID = GetEffectiveRightsRequestControl.OID;
        }
        if (idx < 0) {
            return GenericControl.newControl(controlOID);
        }
        final String remainder = argString.substring(idx + 1, argString.length());
        idx = remainder.indexOf(":");
        if (idx == -1) {
            if (remainder.equalsIgnoreCase("true")) {
                controlCriticality = true;
            } else if (remainder.equalsIgnoreCase("false")) {
                controlCriticality = false;
            } else {
                // TODO: I18N
                throw DecodeException.error(LocalizableMessage
                        .raw("Invalid format for criticality value:" + remainder));
            }
            return GenericControl.newControl(controlOID, controlCriticality);
        }
        final String critical = remainder.substring(0, idx);
        if (critical.equalsIgnoreCase("true")) {
            controlCriticality = true;
        } else if (critical.equalsIgnoreCase("false")) {
            controlCriticality = false;
        } else {
            // TODO: I18N
            throw DecodeException.error(LocalizableMessage
                    .raw("Invalid format for criticality value:" + critical));
        }
        final String valString = remainder.substring(idx + 1, remainder.length());
        if (valString.charAt(0) == ':') {
            controlValue = ByteString.valueOf(valString.substring(1, valString.length()));
        } else if (valString.charAt(0) == '<') {
            // Read data from the file.
            final String filePath = valString.substring(1, valString.length());
            try {
                final byte[] val = readBytesFromFile(filePath);
                controlValue = ByteString.wrap(val);
            } catch (final Exception e) {
                return null;
            }
        } else {
            controlValue = ByteString.valueOf(valString);
        }
        return GenericControl.newControl(controlOID, controlCriticality, controlValue);
    }
    /**
     * Prints a multi-line error message with the provided information to the
     * given print stream.
     *
     * @param app
     *            The console app to use to write the error message.
     * @param ere
     *            The error result.
     * @return The error code.
     */
    static int printErrorMessage(final ConsoleApplication app, final ErrorResultException ere) {
        // if ((ere.getMessage() != null) && (ere.getMessage().length() >
        // 0))
        // {
        // app.println(LocalizableMessage.raw(ere.getMessage()));
        // }
        if (ere.getResult().getResultCode().intValue() >= 0) {
            app.println(ERR_TOOL_RESULT_CODE.get(ere.getResult().getResultCode().intValue(), ere
                    .getResult().getResultCode().toString()));
        }
        if ((ere.getResult().getDiagnosticMessage() != null)
                && (ere.getResult().getDiagnosticMessage().length() > 0)) {
            app.println(ERR_TOOL_ERROR_MESSAGE.get(ere.getResult().getDiagnosticMessage()));
        }
        if (ere.getResult().getMatchedDN() != null && ere.getResult().getMatchedDN().length() > 0) {
            app.println(ERR_TOOL_MATCHED_DN.get(ere.getResult().getMatchedDN()));
        }
        if (app.isVerbose() && ere.getResult().getCause() != null) {
            ere.getResult().getCause().printStackTrace(app.getErrorStream());
        }
        return ere.getResult().getResultCode().intValue();
    }
    static void printPasswordPolicyResults(final ConsoleApplication app, final Connection connection) {
        if (connection instanceof AuthenticatedConnection) {
            final AuthenticatedConnection conn = (AuthenticatedConnection) connection;
            final BindResult result = conn.getAuthenticatedBindResult();
            try {
                final AuthorizationIdentityResponseControl control =
                        result.getControl(AuthorizationIdentityResponseControl.DECODER,
                                new DecodeOptions());
                if (control != null) {
                    final LocalizableMessage message =
                            INFO_BIND_AUTHZID_RETURNED.get(control.getAuthorizationID());
                    app.println(message);
                }
            } catch (final DecodeException e) {
                app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
            }
            try {
                final PasswordExpiredResponseControl control =
                        result.getControl(PasswordExpiredResponseControl.DECODER,
                                new DecodeOptions());
                if (control != null) {
                    final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get();
                    app.println(message);
                }
            } catch (final DecodeException e) {
                app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
            }
            try {
                final PasswordExpiringResponseControl control =
                        result.getControl(PasswordExpiringResponseControl.DECODER,
                                new DecodeOptions());
                if (control != null) {
                    final LocalizableMessage timeString =
                            Utils.secondsToTimeString(control.getSecondsUntilExpiration());
                    final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING.get(timeString);
                    app.println(message);
                }
            } catch (final DecodeException e) {
                app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
            }
            try {
                final PasswordPolicyResponseControl control =
                        result.getControl(PasswordPolicyResponseControl.DECODER,
                                new DecodeOptions());
                if (control != null) {
                    final PasswordPolicyErrorType errorType = control.getErrorType();
                    if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) {
                        final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get();
                        app.println(message);
                    } else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED) {
                        final LocalizableMessage message = INFO_BIND_ACCOUNT_LOCKED.get();
                        app.println(message);
                    } else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET) {
                        final LocalizableMessage message = INFO_BIND_MUST_CHANGE_PASSWORD.get();
                        app.println(message);
                    }
                    final PasswordPolicyWarningType warningType = control.getWarningType();
                    if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) {
                        final LocalizableMessage timeString =
                                Utils.secondsToTimeString(control.getWarningValue());
                        final LocalizableMessage message =
                                INFO_BIND_PASSWORD_EXPIRING.get(timeString);
                        app.println(message);
                    } else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING) {
                        final LocalizableMessage message =
                                INFO_BIND_GRACE_LOGINS_REMAINING.get(control.getWarningValue());
                        app.println(message);
                    }
                }
            } catch (final DecodeException e) {
                app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage()));
            }
        }
    }
    /**
     * Read the data from the specified file and return it in a byte array.
     *
     * @param filePath
     *            The path to the file that should be read.
     * @return A byte array containing the contents of the requested file.
     * @throws IOException
     *             If a problem occurs while trying to read the specified file.
     */
    static byte[] readBytesFromFile(final String filePath) throws IOException {
        byte[] val = null;
        FileInputStream fis = null;
        try {
            final File file = new File(filePath);
            fis = new FileInputStream(file);
            final long length = file.length();
            val = new byte[(int) length];
            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset < val.length
                    && (numRead = fis.read(val, offset, val.length - offset)) >= 0) {
                offset += numRead;
            }
            // Ensure all the bytes have been read in
            if (offset < val.length) {
                throw new IOException("Could not completely read file " + filePath);
            }
            return val;
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
    }
    /**
     * Retrieves a user-friendly string that indicates the length of time (in
     * days, hours, minutes, and seconds) in the specified number of seconds.
     *
     * @param numSeconds
     *            The number of seconds to be converted to a more user-friendly
     *            value.
     * @return The user-friendly representation of the specified number of
     *         seconds.
     */
    static LocalizableMessage secondsToTimeString(final int numSeconds) {
        if (numSeconds < 60) {
            // We can express it in seconds.
            return INFO_TIME_IN_SECONDS.get(numSeconds);
        } else if (numSeconds < 3600) {
            // We can express it in minutes and seconds.
            final int m = numSeconds / 60;
            final int s = numSeconds % 60;
            return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
        } else if (numSeconds < 86400) {
            // We can express it in hours, minutes, and seconds.
            final int h = numSeconds / 3600;
            final int m = (numSeconds % 3600) / 60;
            final int s = numSeconds % 3600 % 60;
            return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
        } else {
            // We can express it in days, hours, minutes, and seconds.
            final int d = numSeconds / 86400;
            final int h = (numSeconds % 86400) / 3600;
            final int m = (numSeconds % 86400 % 3600) / 60;
            final int s = numSeconds % 86400 % 3600 % 60;
            return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
        }
    }
    /**
     * Sets default system property settings for the xxxrate performance tools.
     */
    static void setDefaultPerfToolProperties() {
        // Use SameThreadStrategy by default.
        if (System.getProperty("org.forgerock.opendj.transport.useWorkerThreads") == null) {
            System.setProperty("org.forgerock.opendj.transport.useWorkerThreads", "false");
        }
        // Configure connections to be terminate immediately after closing (this
        // prevents port exhaustion in xxxrate tools when
        // connecting/disconnecting).
        if (System.getProperty("org.forgerock.opendj.transport.linger") == null) {
            System.setProperty("org.forgerock.opendj.transport.linger", "0");
        }
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     *
     * @param message
     *            The message to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point.
     * @return The wrapped text.
     */
    static String wrapText(final LocalizableMessage message, final int width) {
        return wrapText(message.toString(), width, 0);
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     * In addition each line will be indented by the specified amount.
     *
     * @param message
     *            The message to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point (including any indentation).
     * @param indent
     *            The number of columns to indent each line.
     * @return The wrapped text.
     */
    static String wrapText(final LocalizableMessage message, final int width, final int indent) {
        return wrapText(message.toString(), width, indent);
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     *
     * @param text
     *            The text to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point.
     * @return The wrapped text.
     */
    static String wrapText(final String text, final int width) {
        return wrapText(text, width, 0);
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     * In addition each line will be indented by the specified amount.
     *
     * @param text
     *            The text to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point (including any indentation).
     * @param indent
     *            The number of columns to indent each line.
     * @return The wrapped text.
     */
    static String wrapText(final String text, int width, final int indent) {
        // Calculate the real width and indentation padding.
        width -= indent;
        final StringBuilder pb = new StringBuilder();
        for (int i = 0; i < indent; i++) {
            pb.append(' ');
        }
        final String padding = pb.toString();
        final StringBuilder buffer = new StringBuilder();
        if (text != null) {
            final StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
            while (lineTokenizer.hasMoreTokens()) {
                final String line = lineTokenizer.nextToken();
                if (line.equals("\r") || line.equals("\n")) {
                    // It's an end-of-line character, so append it as-is.
                    buffer.append(line);
                } else if (line.length() <= width) {
                    // The line fits in the specified width, so append it as-is.
                    buffer.append(padding);
                    buffer.append(line);
                } else {
                    // The line doesn't fit in the specified width, so it needs
                    // to
                    // be
                    // wrapped. Do so at space boundaries.
                    StringBuilder lineBuffer = new StringBuilder();
                    StringBuilder delimBuffer = new StringBuilder();
                    final StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
                    while (wordTokenizer.hasMoreTokens()) {
                        final String word = wordTokenizer.nextToken();
                        if (word.equals(" ")) {
                            // It's a space, so add it to the delim buffer only
                            // if the
                            // line
                            // buffer is not empty.
                            if (lineBuffer.length() > 0) {
                                delimBuffer.append(word);
                            }
                        } else if (word.length() > width) {
                            // This is a long word that can't be wrapped, so
                            // we'll
                            // just have
                            // to make do.
                            if (lineBuffer.length() > 0) {
                                buffer.append(padding);
                                buffer.append(lineBuffer);
                                buffer.append(EOL);
                                lineBuffer = new StringBuilder();
                            }
                            buffer.append(padding);
                            buffer.append(word);
                            if (wordTokenizer.hasMoreTokens()) {
                                // The next token must be a space, so remove it.
                                // If
                                // there are
                                // still more tokens after that, then append an
                                // EOL.
                                wordTokenizer.nextToken();
                                if (wordTokenizer.hasMoreTokens()) {
                                    buffer.append(EOL);
                                }
                            }
                            if (delimBuffer.length() > 0) {
                                delimBuffer = new StringBuilder();
                            }
                        } else {
                            // It's not a space, so see if we can fit it on the
                            // curent
                            // line.
                            final int newLineLength =
                                    lineBuffer.length() + delimBuffer.length() + word.length();
                            if (newLineLength < width) {
                                // It does fit on the line, so add it.
                                lineBuffer.append(delimBuffer).append(word);
                                if (delimBuffer.length() > 0) {
                                    delimBuffer = new StringBuilder();
                                }
                            } else {
                                // It doesn't fit on the line, so end the
                                // current line
                                // and start
                                // a new one.
                                buffer.append(padding);
                                buffer.append(lineBuffer);
                                buffer.append(EOL);
                                lineBuffer = new StringBuilder();
                                lineBuffer.append(word);
                                if (delimBuffer.length() > 0) {
                                    delimBuffer = new StringBuilder();
                                }
                            }
                        }
                    }
                    // If there's anything left in the line buffer, then add it
                    // to
                    // the
                    // final buffer.
                    buffer.append(padding);
                    buffer.append(lineBuffer);
                }
            }
        }
        return buffer.toString();
    }
    // Prevent instantiation.
    private Utils() {
        // Do nothing.
    }
}