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

neil_a_wilson
28.15.2007 5133f4340c33425a6a2cba4b8764d6e684b37da4
Add a new extended operation that can be used to interact with password policy
state information for a user, including getting and setting various state
variables (even those marked NO-USER-MODIFICATION, although these are typically
intended for testing purposes only). Users will be required to have the
password-reset privilege in order to be able to use this extended operation,
and access control will also come into play.

Also, include a manage-account tool that can be used to interact with this
extended operation. It supports a subset of the capabilities made available in
the extended operation (e.g., it only allows one operation per use, whereas the
extended operation allows you to include multiple operations), and some of the
features intended for testing purposes are marked hidden so that they aren't
readily apparent to end users.

Finally, fix a bug in which it was not possible for users to authenticate if
their account had the pwdReset flag and last login time tracking was enabled.

OpenDS Issue Numbers: 292, 579, 1782, 1845
5 files added
7 files modified
6552 ■■■■■ changed files
opends/resource/bin/manage-account 37 ●●●●● patch | view | raw | blame | history
opends/resource/bin/manage-account.bat 33 ●●●●● patch | view | raw | blame | history
opends/resource/config/config.ldif 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ModifyOperation.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PasswordPolicyState.java 552 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/PasswordPolicyStateExtendedOperation.java 1703 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/ExtensionsMessages.java 383 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/ToolMessages.java 1184 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/ManageAccount.java 1688 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/ServerConstants.java 9 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java 80 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/tools/ManageAccountTestCase.java 871 ●●●●● patch | view | raw | blame | history
opends/resource/bin/manage-account
New file
@@ -0,0 +1,37 @@
#!/bin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License").  You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at
# trunk/opends/resource/legal-notices/OpenDS.LICENSE
# or https://OpenDS.dev.java.net/OpenDS.LICENSE.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at
# trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
# add the following below this CDDL HEADER, with the fields enclosed
# by brackets "[]" replaced with your own identifying information:
#      Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
# This script may be used to perform LDAP search operations.
OPENDS_INVOKE_CLASS="org.opends.server.tools.PasswordPolicyState"
export OPENDS_INVOKE_CLASS
SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=password-policy-state"
export SCRIPT_NAME_ARG
SCRIPT_DIR=`dirname "${0}"`
"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
opends/resource/bin/manage-account.bat
New file
@@ -0,0 +1,33 @@
@echo off
rem CDDL HEADER START
rem
rem The contents of this file are subject to the terms of the
rem Common Development and Distribution License, Version 1.0 only
rem (the "License").  You may not use this file except in compliance
rem with the License.
rem
rem You can obtain a copy of the license at
rem trunk/opends/resource/legal-notices/OpenDS.LICENSE
rem or https://OpenDS.dev.java.net/OpenDS.LICENSE.
rem See the License for the specific language governing permissions
rem and limitations under the License.
rem
rem When distributing Covered Code, include this CDDL HEADER in each
rem file and include the License file at
rem trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
rem add the following below this CDDL HEADER, with the fields enclosed
rem by brackets "[]" replaced with your own identifying information:
rem      Portions Copyright [yyyy] [name of copyright owner]
rem
rem CDDL HEADER END
rem
rem
rem      Portions Copyright 2006-2007 Sun Microsystems, Inc.
setlocal
set OPENDS_INVOKE_CLASS="org.opends.server.tools.PasswordPolicyState"
set SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=password-policy-state"
call "%~dP0\..\lib\_client-script.bat" %*
opends/resource/config/config.ldif
@@ -406,6 +406,13 @@
ds-cfg-extended-operation-handler-enabled: true
ds-cfg-identity-mapper-dn: cn=Exact Match,cn=Identity Mappers,cn=config
dn: cn=Password Policy State,cn=Extended Operations,cn=config
objectClass: top
objectCLass: ds-cfg-extended-operation-handler
cn: Password Policy State
ds-cfg-extended-operation-handler-class: org.opends.server.extensions.PasswordPolicyStateExtendedOperation
ds-cfg-extended-operation-handler-enabled: true
dn: cn=StartTLS,cn=Extended Operations,cn=config
objectClass: top
objectClass: ds-cfg-extended-operation-handler
opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -810,7 +810,7 @@
      // If the user must change their password before doing anything else, and
      // if the target of the modify operation isn't the user's own entry, then
      // reject the request.
      if (clientConnection.mustChangePassword())
      if ((! isInternalOperation()) && clientConnection.mustChangePassword())
      {
        DN authzDN = getAuthorizationDN();
        if ((authzDN != null) && (! authzDN.equals(entryDN)))
@@ -2450,7 +2450,8 @@
            break modifyProcessing;
          }
        }
        else if(pwPolicyState.mustChangePassword())
        else if ((! isInternalOperation()) &&
                 pwPolicyState.mustChangePassword())
        {
          // The user will not be allowed to do anything else before
          // the password gets changed.
opends/src/server/org/opends/server/core/PasswordPolicyState.java
@@ -31,6 +31,7 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -147,8 +148,8 @@
  // The set of grace login times for this user.
  private List<Long> graceLoginTimes = null;
  // The time that the user's password should expire (or did expire).
  private long expirationTime = Long.MIN_VALUE;
  // The time that the user's account should expire (or did expire).
  private long accountExpirationTime = Long.MIN_VALUE;
  // The time that the user's entry was locked due to too many authentication
  // failures.
@@ -157,6 +158,9 @@
  // The time that the user last authenticated to the Directory Server.
  private long lastLoginTime = Long.MIN_VALUE;
  // The time that the user's password should expire (or did expire).
  private long passwordExpirationTime = Long.MIN_VALUE;
  // The last required change time with which the user complied.
  private long requiredChangeTime = Long.MIN_VALUE;
@@ -187,13 +191,42 @@
                             boolean debug)
       throws DirectoryException
  {
    this(userEntry, updateEntry, TimeThread.getTime(), debug);
  }
  /**
   * Creates a new password policy state object with the provided information.
   * Note that this version of the constructor should only be used for testing
   * purposes when the tests should be evaluated with a fixed time rather than
   * the actual current time.  For all other purposes, the other constructor
   * should be used.
   *
   * @param  userEntry    The entry with the user account.
   * @param  updateEntry  Indicates whether changes should update the provided
   *                      user entry directly or whether they should be
   *                      collected as a set of modifications.
   * @param  currentTime  The time to use as the current time for all
   *                      time-related determinations.
   * @param  debug        Indicates whether to enable debugging for the
   *                      operations performed.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to
   *                              determine the password policy for the user or
   *                              perform any other state initialization.
   */
  public PasswordPolicyState(Entry userEntry, boolean updateEntry,
                             long currentTime, boolean debug)
       throws DirectoryException
  {
    this.userEntry   = userEntry;
    this.updateEntry = updateEntry;
    this.debug       = debug;
    this.currentTime = currentTime;
    userDNString     = userEntry.getDN().toString();
    passwordPolicy   = getPasswordPolicyInternal(this.userEntry, this.debug);
    currentTime      = TimeThread.getTime();
    // Get the password changed time for the user.
    AttributeType type
@@ -636,6 +669,30 @@
  /**
   * Retrieves the time that the password was last changed.
   *
   * @return  The time that the password was last changed.
   */
  public long getPasswordChangedTime()
  {
    return passwordChangedTime;
  }
  /**
   * Retrieves the time that this password policy state object was created.
   *
   * @return  The time that this password policy state object was created.
   */
  public long getCurrentTime()
  {
    return currentTime;
  }
  /**
   * Retrieves the set of values for the password attribute from the user entry.
   *
   * @return  The set of values for the password attribute from the user entry.
@@ -664,6 +721,20 @@
   */
  public void setPasswordChangedTime()
  {
    setPasswordChangedTime(currentTime);
  }
  /**
   * Sets a new value for the password changed time equal to the specified time.
   * This method should generally only be used for testing purposes, since the
   * variant that uses the current time is preferred almost everywhere else.
   *
   * @param  passwordChangedTime  The time to use
   */
  public void setPasswordChangedTime(long passwordChangedTime)
  {
    if (debug)
    {
      if (debugEnabled())
@@ -675,9 +746,9 @@
    // passwordChangedTime is computed in the constructor from values in the
    // entry.
    if (passwordChangedTime != currentTime)
    if (this.passwordChangedTime != passwordChangedTime)
    {
      passwordChangedTime = currentTime;
      this.passwordChangedTime = passwordChangedTime;
      AttributeType type =
           DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC);
@@ -693,11 +764,11 @@
      values.add(new AttributeValue(type, timeValue));
      Attribute a = new Attribute(type, OP_ATTR_PWPOLICY_CHANGED_TIME, values);
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(a);
      if (updateEntry)
      {
        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
        attrList.add(a);
        userEntry.putAttribute(type, attrList);
      }
      else
@@ -710,6 +781,57 @@
  /**
   * Removes the password changed time value from the user's entry.  This should
   * only be used for testing purposes, as it can really mess things up if you
   * don't know what you're doing.
   */
  public void clearPasswordChangedTime()
  {
    if (debug)
    {
      if (debugEnabled())
      {
        TRACER.debugInfo("Clearing password changed time for user %s",
                         userDNString);
      }
    }
    AttributeType type =
         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC,
                                       true);
    if (updateEntry)
    {
      userEntry.removeAttribute(type);
    }
    else
    {
      Attribute a = new Attribute(type);
      modifications.add(new Modification(ModificationType.REPLACE, a, true));
    }
    // Fall back to using the entry creation time as the password changed time,
    // if it's defined.  Otherwise, use a value of zero.
    AttributeType createTimeType =
         DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true);
    try
    {
      passwordChangedTime = getGeneralizedTime(createTimeType);
      if (passwordChangedTime <= 0)
      {
        passwordChangedTime = 0;
      }
    }
    catch (Exception e)
    {
      passwordChangedTime = 0;
    }
  }
  /**
   * Indicates whether the user account has been administratively disabled.
   *
   * @return  <CODE>true</CODE> if the user account has been administratively
@@ -876,10 +998,9 @@
         DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
                                          true);
    long expirationTime;
    try
    {
      expirationTime = getGeneralizedTime(type);
      accountExpirationTime = getGeneralizedTime(type);
     }
    catch (Exception e)
    {
@@ -900,7 +1021,7 @@
      return true;
    }
    if (expirationTime > currentTime)
    if (accountExpirationTime > currentTime)
    {
      // The user does have an expiration time, but it hasn't arrived yet.
      isAccountExpired = ConditionResult.FALSE;
@@ -913,7 +1034,7 @@
        }
      }
    }
    else if (expirationTime >= 0)
    else if (accountExpirationTime >= 0)
    {
      // The user does have an expiration time, and it is in the past.
      isAccountExpired = ConditionResult.TRUE;
@@ -948,6 +1069,109 @@
  /**
   * Retrieves the time at which the user's account will expire.
   *
   * @return  The time at which the user's account will expire, or -1 if it is
   *          not configured with an expiration time.
   */
  public long getAccountExpirationTime()
  {
    if (accountExpirationTime == Long.MIN_VALUE)
    {
      isAccountExpired();
    }
    return accountExpirationTime;
  }
  /**
   * Sets the user's account expiration time to the specified value.
   *
   * @param  accountExpirationTime  The time that the user's account should
   *                                expire.
   */
  public void setAccountExpirationTime(long accountExpirationTime)
  {
    if (accountExpirationTime < 0)
    {
      clearAccountExpirationTime();
    }
    else
    {
      String timeStr = GeneralizedTimeSyntax.format(accountExpirationTime);
      if (debug)
      {
        if (debugEnabled())
        {
          TRACER.debugInfo("Setting account expiration time for user %s to %s",
                    userDNString, timeStr);
        }
      }
      this.accountExpirationTime = accountExpirationTime;
      AttributeType type =
           DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
                                            true);
      LinkedHashSet<AttributeValue> values =
           new LinkedHashSet<AttributeValue>(1);
      values.add(new AttributeValue(type, timeStr));
      Attribute a = new Attribute(type, OP_ATTR_ACCOUNT_EXPIRATION_TIME,
                                  values);
      if (updateEntry)
      {
        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
        attrList.add(a);
        userEntry.putAttribute(type, attrList);
      }
      else
      {
        modifications.add(new Modification(ModificationType.REPLACE, a, true));
      }
    }
  }
  /**
   * Clears the user's account expiration time.
   */
  public void clearAccountExpirationTime()
  {
    if (debug)
    {
      if (debugEnabled())
      {
        TRACER.debugInfo("Clearing account expiration time for user %s",
                  userDNString);
      }
    }
    accountExpirationTime = -1;
    AttributeType type =
         DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
                                          true);
    if (updateEntry)
    {
      userEntry.removeAttribute(type);
    }
    else
    {
      modifications.add(new Modification(ModificationType.REPLACE,
                                         new Attribute(type), true));
    }
  }
  /**
   * Retrieves the set of times of failed authentication attempts for the user.
   * If authentication failure time expiration is enabled, and there are expired
   * times in the entry, these times are removed from the instance field and an
@@ -957,7 +1181,7 @@
   *          which will be an empty list in the case of no valid (unexpired)
   *          times in the entry.
   */
  private List<Long> getAuthFailureTimes()
  public List<Long> getAuthFailureTimes()
  {
    if (authFailureTimes != null)
    {
@@ -1185,7 +1409,75 @@
    // Now check to see if there have been sufficient failures to lock the
    // account.
    if (passwordPolicy.getLockoutFailureCount() <= failureTimes.size())
    int lockoutCount = passwordPolicy.getLockoutFailureCount();
    if ((lockoutCount > 0) && (lockoutCount <= authFailureTimes.size()))
    {
      setFailureLockedTime(highestFailureTime);
      if (debug)
      {
        if (debugEnabled())
        {
          TRACER.debugInfo("Locking user account %s due to too many failures.",
                    userDNString);
        }
      }
    }
  }
  /**
   * Explicitly specifies the auth failure times for the associated user.  This
   * should generally only be used for testing purposes.  Note that it will also
   * set or clear the locked time as appropriate.
   *
   * @param  authFailureTimes  The set of auth failure times to use for the
   *                           account.  An empty list or {@code null} will
   *                           clear the account of any existing failures.
   */
  public void setAuthFailureTimes(List<Long> authFailureTimes)
  {
    if ((authFailureTimes == null) || authFailureTimes.isEmpty())
    {
      clearAuthFailureTimes();
      clearFailureLockedTime();
      return;
    }
    long highestFailureTime = -1;
    for (Long l : authFailureTimes)
    {
      highestFailureTime = Math.max(l, highestFailureTime);
    }
    AttributeType type =
         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC,
                                          true);
    LinkedHashSet<AttributeValue> values =
           new LinkedHashSet<AttributeValue>(authFailureTimes.size());
    for (Long l : authFailureTimes)
    {
      values.add(new AttributeValue(type, GeneralizedTimeSyntax.format(l)));
    }
    Attribute a = new Attribute(type, OP_ATTR_PWPOLICY_FAILURE_TIME, values);
    if (updateEntry)
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(a);
      userEntry.putAttribute(type, attrList);
    }
    else
    {
      modifications.add(new Modification(ModificationType.REPLACE, a, true));
    }
    // Now check to see if there have been sufficient failures to lock the
    // account.
    int lockoutCount = passwordPolicy.getLockoutFailureCount();
    if ((lockoutCount > 0) && (lockoutCount <= authFailureTimes.size()))
    {
      setFailureLockedTime(highestFailureTime);
      if (debug)
@@ -1686,6 +1978,20 @@
   */
  public void setLastLoginTime()
  {
    setLastLoginTime(currentTime);
  }
  /**
   * Updates the user entry to use the specified last login time.  This should
   * be used primarily for testing purposes, as the variant that uses the
   * current time should be used most of the time.
   *
   * @param  lastLoginTime  The last login time to set in the user entry.
   */
  public void setLastLoginTime(long lastLoginTime)
  {
    AttributeType type = passwordPolicy.getLastLoginTimeAttribute();
    String format = passwordPolicy.getLastLoginTimeFormat();
@@ -1698,7 +2004,8 @@
    try
    {
      SimpleDateFormat dateFormat = new SimpleDateFormat(format);
      timestamp = dateFormat.format(TimeThread.getDate());
      timestamp = dateFormat.format(new Date(lastLoginTime));
      this.lastLoginTime = dateFormat.parse(timestamp).getTime();
    }
    catch (Exception e)
    {
@@ -1764,6 +2071,38 @@
  /**
   * Clears the last login time from the user's entry.  This should generally be
   * used only for testing purposes.
   */
  public void clearLastLoginTime()
  {
    if (debug)
    {
      if (debugEnabled())
      {
        TRACER.debugInfo("Clearing last login time for user %s", userDNString);
      }
    }
    lastLoginTime = -1;
    AttributeType type =
         DirectoryServer.getAttributeType(OP_ATTR_LAST_LOGIN_TIME, true);
    if (updateEntry)
    {
      userEntry.removeAttribute(type);
    }
    else
    {
      modifications.add(new Modification(ModificationType.REPLACE,
                                         new Attribute(type), true));
    }
  }
  /**
   * Indicates whether the user's account is currently locked because it has
   * been idle for too long.
   *
@@ -2108,9 +2447,9 @@
   */
  public long getPasswordExpirationTime()
  {
    if (expirationTime == Long.MIN_VALUE)
    if (passwordExpirationTime == Long.MIN_VALUE)
    {
      expirationTime = Long.MAX_VALUE;
      passwordExpirationTime = Long.MAX_VALUE;
      boolean checkWarning = false;
@@ -2118,9 +2457,9 @@
      if (maxAge > 0)
      {
        long expTime = passwordChangedTime + (1000L*maxAge);
        if (expTime < expirationTime)
        if (expTime < passwordExpirationTime)
        {
          expirationTime = expTime;
          passwordExpirationTime = expTime;
          checkWarning   = true;
        }
      }
@@ -2129,9 +2468,9 @@
      if (mustChangePassword() && (maxResetAge > 0))
      {
        long expTime = passwordChangedTime + (1000L*maxResetAge);
        if (expTime < expirationTime)
        if (expTime < passwordExpirationTime)
        {
          expirationTime = expTime;
          passwordExpirationTime = expTime;
          checkWarning   = false;
        }
      }
@@ -2141,20 +2480,20 @@
      {
        long reqChangeTime = getRequiredChangeTime();
        if ((reqChangeTime != mustChangeTime) &&
            (mustChangeTime < expirationTime))
            (mustChangeTime < passwordExpirationTime))
        {
          expirationTime = mustChangeTime;
          passwordExpirationTime = mustChangeTime;
          checkWarning   = true;
        }
      }
      if (expirationTime == Long.MAX_VALUE)
      if (passwordExpirationTime == Long.MAX_VALUE)
      {
        expirationTime    = -1;
        shouldWarn        = ConditionResult.FALSE;
        isFirstWarning    = ConditionResult.FALSE;
        isPasswordExpired = ConditionResult.FALSE;
        mayUseGraceLogin  = ConditionResult.TRUE;
        passwordExpirationTime = -1;
        shouldWarn             = ConditionResult.FALSE;
        isFirstWarning         = ConditionResult.FALSE;
        isPasswordExpired      = ConditionResult.FALSE;
        mayUseGraceLogin       = ConditionResult.TRUE;
      }
      else if (checkWarning)
      {
@@ -2163,7 +2502,8 @@
        int warningInterval = passwordPolicy.getWarningInterval();
        if (warningInterval > 0)
        {
          long shouldWarnTime = expirationTime - (warningInterval*1000L);
          long shouldWarnTime =
                    passwordExpirationTime - (warningInterval*1000L);
          if (shouldWarnTime > currentTime)
          {
            // The warning time is in the future, so we know the password isn't
@@ -2178,7 +2518,7 @@
            // expired.
            long warnedTime = getWarnedTime();
            if (expirationTime > currentTime)
            if (passwordExpirationTime > currentTime)
            {
              // The password is not expired but we should warn the user.
              shouldWarn        = ConditionResult.TRUE;
@@ -2191,7 +2531,8 @@
                if (! passwordPolicy.expirePasswordsWithoutWarning())
                {
                  expirationTime = currentTime + (warningInterval*1000L);
                  passwordExpirationTime =
                       currentTime + (warningInterval*1000L);
                }
              }
              else
@@ -2200,7 +2541,7 @@
                if (! passwordPolicy.expirePasswordsWithoutWarning())
                {
                  expirationTime = warnedTime + (warningInterval*1000L);
                  passwordExpirationTime = warnedTime + (warningInterval*1000L);
                }
              }
            }
@@ -2216,8 +2557,8 @@
              }
              else if (warnedTime > 0)
              {
                expirationTime = warnedTime + (warningInterval*1000L);
                if (expirationTime > currentTime)
                passwordExpirationTime = warnedTime + (warningInterval*1000L);
                if (passwordExpirationTime > currentTime)
                {
                  shouldWarn        = ConditionResult.TRUE;
                  isFirstWarning    = ConditionResult.FALSE;
@@ -2232,10 +2573,10 @@
              }
              else
              {
                shouldWarn        = ConditionResult.TRUE;
                isFirstWarning    = ConditionResult.TRUE;
                isPasswordExpired = ConditionResult.FALSE;
                expirationTime    = currentTime + (warningInterval*1000L);
                shouldWarn             = ConditionResult.TRUE;
                isFirstWarning         = ConditionResult.TRUE;
                isPasswordExpired      = ConditionResult.FALSE;
                passwordExpirationTime = currentTime + (warningInterval*1000L);
              }
            }
          }
@@ -2247,7 +2588,7 @@
          shouldWarn     = ConditionResult.FALSE;
          isFirstWarning = ConditionResult.FALSE;
          if (currentTime > expirationTime)
          if (currentTime > passwordExpirationTime)
          {
            isPasswordExpired = ConditionResult.TRUE;
          }
@@ -2263,7 +2604,7 @@
        shouldWarn       = ConditionResult.FALSE;
        isFirstWarning   = ConditionResult.FALSE;
        if (expirationTime < currentTime)
        if (passwordExpirationTime < currentTime)
        {
          isPasswordExpired = ConditionResult.TRUE;
        }
@@ -2279,11 +2620,11 @@
      if (debugEnabled())
      {
        TRACER.debugInfo("Returning password expiration time of %d for user " +
            "%s.", expirationTime, userDNString);
            "%s.", passwordExpirationTime, userDNString);
      }
    }
    return expirationTime;
    return passwordExpirationTime;
  }
@@ -2538,6 +2879,24 @@
   */
  public void setRequiredChangeTime()
  {
    long requiredChangeByTimePolicy = passwordPolicy.getRequireChangeByTime();
    if (requiredChangeByTimePolicy > 0)
    {
      setRequiredChangeTime(requiredChangeByTimePolicy);
    }
  }
  /**
   * Updates the user entry with a timestamp indicating that the password has
   * been changed in accordance with the require change time.
   *
   * @param  requiredChangeTime  The timestamp to use for the required change
   *                             time value.
   */
  public void setRequiredChangeTime(long requiredChangeTime)
  {
    if (debug)
    {
      if (debugEnabled())
@@ -2547,16 +2906,14 @@
      }
    }
    long requiredChangeByTimePolicy = passwordPolicy.getRequireChangeByTime();
    if (getRequiredChangeTime() != requiredChangeByTimePolicy)
    if (getRequiredChangeTime() != requiredChangeTime)
    {
      AttributeType type = DirectoryServer.getAttributeType(
                               OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true);
      LinkedHashSet<AttributeValue> values =
           new LinkedHashSet<AttributeValue>(1);
      String timeValue =
           GeneralizedTimeSyntax.format(requiredChangeByTimePolicy);
      String timeValue = GeneralizedTimeSyntax.format(requiredChangeTime);
      values.add(new AttributeValue(type, timeValue));
      Attribute a = new Attribute(type,
@@ -2579,6 +2936,36 @@
  /**
   * Updates the user entry to remove any timestamp indicating that the password
   * has been changed in accordance with the required change time.
   */
  public void clearRequiredChangeTime()
  {
    if (debug)
    {
      if (debugEnabled())
      {
        TRACER.debugInfo("Clearing required change time for user %s",
                         userDNString);
      }
    }
    AttributeType type = DirectoryServer.getAttributeType(
                             OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true);
    if (updateEntry)
    {
      userEntry.removeAttribute(type);
    }
    else
    {
      modifications.add(new Modification(ModificationType.REPLACE,
                                         new Attribute(type), true));
    }
  }
  /**
   * Retrieves the time that the user was first warned about an upcoming
   * expiration.
   *
@@ -2632,22 +3019,37 @@
   */
  public void setWarnedTime()
  {
    setWarnedTime(currentTime);
  }
  /**
   * Updates the user entry to set the warned time to the specified time.  This
   * method should generally only be used for testing purposes, since the
   * variant that uses the current time is preferred almost everywhere else.
   *
   * @param  warnedTime  The value to use for the warned time.
   */
  public void setWarnedTime(long warnedTime)
  {
    long warnTime = getWarnedTime();
    if (warnTime == currentTime)
    if (warnTime == warnedTime)
    {
      if (debug)
      {
        if (debugEnabled())
        {
          TRACER.debugInfo("Not updating warned time for user %s because " +
              "the warned time is the same as the current time.", userDNString);
              "the warned time is the same as the specified time.",
              userDNString);
        }
      }
      return;
    }
    warnedTime = currentTime;
    this.warnedTime = warnedTime;
    AttributeType type =
         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true);
@@ -2879,6 +3281,58 @@
  /**
   * Specifies the set of grace login use times for the associated user.  If
   * the provided list is empty or {@code null}, then the set will be cleared.
   *
   * @param  graceLoginTimes  The grace login use times for the associated user.
   */
  public void setGraceLoginTimes(List<Long> graceLoginTimes)
  {
    if ((graceLoginTimes == null) || graceLoginTimes.isEmpty())
    {
      clearGraceLoginTimes();
      return;
    }
    if (debug)
    {
      if (debugEnabled())
      {
        TRACER.debugInfo("Updating grace login times for user %s",
                         userDNString);
      }
    }
    AttributeType type =
         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC,
                                          true);
    LinkedHashSet<AttributeValue> values =
         new LinkedHashSet<AttributeValue>(graceLoginTimes.size());
    for (Long l : graceLoginTimes)
    {
      values.add(new AttributeValue(type, GeneralizedTimeSyntax.format(l)));
    }
    Attribute a =
         new Attribute(type, OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME, values);
    if (updateEntry)
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(a);
      userEntry.putAttribute(type, attrList);
    }
    else
    {
      modifications.add(new Modification(ModificationType.REPLACE, a, true));
    }
  }
  /**
   * Updates the user entry to remove any record of previous grace logins.
   */
  public void clearGraceLoginTimes()
opends/src/server/org/opends/server/extensions/PasswordPolicyStateExtendedOperation.java
New file
@@ -0,0 +1,1703 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ExtendedOperationHandler;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.PasswordPolicy;
import org.opends.server.core.PasswordPolicyState;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.asn1.ASN1Element;
import org.opends.server.protocols.asn1.ASN1Enumerated;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.asn1.ASN1Sequence;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.schema.GeneralizedTimeSyntax;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Modification;
import org.opends.server.types.Privilege;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchScope;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.ExtensionsMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class implements an LDAP extended operation that can be used to query
 * and update elements of the Directory Server password policy state for a given
 * user.  The ASN.1 definition for the value of the extended request is:
 * <BR>
 * <PRE>
 * PasswordPolicyStateValue ::= SEQUENCE {
 *      targetUser     LDAPDN
 *      operations     SEQUENCE OF PasswordPolicyStateOperation OPTIONAL }
 *
 * PasswordPolicyStateOperation ::= SEQUENCE {
 *      opType       ENUMERATED {
 *           getPasswordPolicyDN                          (0),
 *           getAccountDisabledState                      (1),
 *           setAccountDisabledState                      (2),
 *           clearAccountDisabledState                    (3),
 *           getAccountExpirationTime                     (4),
 *           setAccountExpirationTime                     (5),
 *           clearAccountExpirationTime                   (6),
 *           getSecondsUntilAccountExpiration             (7),
 *           getPasswordChangedTime                       (8),
 *           setPasswordChangedTime                       (9),
 *           clearPasswordChangedTime                     (10),
 *           getPasswordExpirationWarnedTime              (11),
 *           setPasswordExpirationWarnedTime              (12),
 *           clearPasswordExpirationWarnedTime            (13),
 *           getSecondsUntilPasswordExpiration            (14),
 *           getSecondsUntilPasswordExpirationWarning     (15),
 *           getAuthenticationFailureTimes                (16),
 *           addAuthenticationFailureTime                 (17),
 *           setAuthenticationFailureTimes                (18),
 *           clearAuthenticationFailureTimes              (19),
 *           getSecondsUntilAuthenticationFailureUnlock   (20),
 *           getRemainingAuthenticationFailureCount       (21),
 *           getLastLoginTime                             (22),
 *           setLastLoginTime                             (23),
 *           clearLastLoginTime                           (24),
 *           getSecondsUntilIdleLockout                   (25),
 *           getPasswordResetState                        (26),
 *           setPasswordResetState                        (27),
 *           clearPasswordResetState                      (28),
 *           getSecondsUntilPasswordResetLockout          (29),
 *           getGraceLoginUseTimes                        (30),
 *           addGraceLoginUseTime                         (31),
 *           setGraceLoginUseTimes                        (32),
 *           clearGraceLoginUseTimes                      (33),
 *           getRemainingGraceLoginCount                  (34),
 *           getPasswordChangedByRequiredTime             (35),
 *           setPasswordChangedByRequiredTime             (36),
 *           clearPasswordChangedByRequiredTime           (37),
 *           getSecondsUntilRequiredChangeTime            (38),
 *           ... },
 *      opValues     SEQUENCE OF OCTET STRING OPTIONAL }
 * </PRE>
 * <BR>
 * Both the request and response values use the same encoded form, and they both
 * use the same OID of "1.3.6.1.4.1.26027.1.6.1".  The response value will only
 * include get* elements.  If the request did not include any operations, then
 * the response will include all get* elements; otherwise, the response will
 * only include the get* elements that correspond to the state fields referenced
 * in the request (regardless of whether that operation was included in a get*,
 * set*, add*, remove*, or clear* operation).
 */
public class PasswordPolicyStateExtendedOperation
       extends ExtendedOperationHandler<ExtendedOperationHandlerCfg>
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * The enumerated value for the getPasswordPolicyDN operation.
   */
  public static final int OP_GET_PASSWORD_POLICY_DN = 0;
  /**
   * The enumerated value for the getAccountDisabledState operation.
   */
  public static final int OP_GET_ACCOUNT_DISABLED_STATE = 1;
  /**
   * The enumerated value for the setAccountDisabledState operation.
   */
  public static final int OP_SET_ACCOUNT_DISABLED_STATE = 2;
  /**
   * The enumerated value for the clearAccountDisabledState operation.
   */
  public static final int OP_CLEAR_ACCOUNT_DISABLED_STATE = 3;
  /**
   * The enumerated value for the getAccountExpirationTime operation.
   */
  public static final int OP_GET_ACCOUNT_EXPIRATION_TIME = 4;
  /**
   * The enumerated value for the setAccountExpirationTime operation.
   */
  public static final int OP_SET_ACCOUNT_EXPIRATION_TIME = 5;
  /**
   * The enumerated value for the clearAccountExpirationTime operation.
   */
  public static final int OP_CLEAR_ACCOUNT_EXPIRATION_TIME = 6;
  /**
   * The enumerated value for the getSecondsUntilAccountExpiration operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION = 7;
  /**
   * The enumerated value for the getPasswordChangedTime operation.
   */
  public static final int OP_GET_PASSWORD_CHANGED_TIME = 8;
  /**
   * The enumerated value for the setPasswordChangedTime operation.
   */
  public static final int OP_SET_PASSWORD_CHANGED_TIME = 9;
  /**
   * The enumerated value for the clearPasswordChangedTime operation.
   */
  public static final int OP_CLEAR_PASSWORD_CHANGED_TIME = 10;
  /**
   * The enumerated value for the getPasswordExpirationWarnedTime operation.
   */
  public static final int OP_GET_PASSWORD_EXPIRATION_WARNED_TIME = 11;
  /**
   * The enumerated value for the setPasswordExpirationWarnedTime operation.
   */
  public static final int OP_SET_PASSWORD_EXPIRATION_WARNED_TIME = 12;
  /**
   * The enumerated value for the clearPasswordExpirationWarnedTime operation.
   */
  public static final int OP_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME = 13;
  /**
   * The enumerated value for the getSecondsUntilPasswordExpiration operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION = 14;
  /**
   * The enumerated value for the getSecondsUntilPasswordExpirationWarning
   * operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING = 15;
  /**
   * The enumerated value for the getAuthenticationFailureTimes operation.
   */
  public static final int OP_GET_AUTHENTICATION_FAILURE_TIMES = 16;
  /**
   * The enumerated value for the addAuthenticationFailureTime operation.
   */
  public static final int OP_ADD_AUTHENTICATION_FAILURE_TIME = 17;
  /**
   * The enumerated value for the setAuthenticationFailureTimes operation.
   */
  public static final int OP_SET_AUTHENTICATION_FAILURE_TIMES = 18;
  /**
   * The enumerated value for the clearAuthenticationFailureTimes operation.
   */
  public static final int OP_CLEAR_AUTHENTICATION_FAILURE_TIMES = 19;
  /**
   * The enumerated value for the getSecondsUntilAuthenticationFailureUnlock
   * operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK =
       20;
  /**
   * The enumerated value for the getRemainingAuthenticationFailureCount
   * operation.
   */
  public static final int OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT = 21;
  /**
   * The enumerated value for the getLastLoginTime operation.
   */
  public static final int OP_GET_LAST_LOGIN_TIME = 22;
  /**
   * The enumerated value for the setLastLoginTime operation.
   */
  public static final int OP_SET_LAST_LOGIN_TIME = 23;
  /**
   * The enumerated value for the clearLastLoginTime operation.
   */
  public static final int OP_CLEAR_LAST_LOGIN_TIME = 24;
  /**
   * The enumerated value for the getSecondsUntilIdleLockout operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT = 25;
  /**
   * The enumerated value for the getPasswordResetState operation.
   */
  public static final int OP_GET_PASSWORD_RESET_STATE = 26;
  /**
   * The enumerated value for the setPasswordResetState operation.
   */
  public static final int OP_SET_PASSWORD_RESET_STATE = 27;
  /**
   * The enumerated value for the clearPasswordResetState operation.
   */
  public static final int OP_CLEAR_PASSWORD_RESET_STATE = 28;
  /**
   * The enumerated value for the getSecondsUntilPasswordResetLockout operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT = 29;
  /**
   * The enumerated value for the getGraceLoginUseTimes operation.
   */
  public static final int OP_GET_GRACE_LOGIN_USE_TIMES = 30;
  /**
   * The enumerated value for the addGraceLoginUseTime operation.
   */
  public static final int OP_ADD_GRACE_LOGIN_USE_TIME = 31;
  /**
   * The enumerated value for the setGraceLoginUseTimes operation.
   */
  public static final int OP_SET_GRACE_LOGIN_USE_TIMES = 32;
  /**
   * The enumerated value for the clearGraceLoginUseTimes operation.
   */
  public static final int OP_CLEAR_GRACE_LOGIN_USE_TIMES = 33;
  /**
   * The enumerated value for the getRemainingGraceLoginCount operation.
   */
  public static final int OP_GET_REMAINING_GRACE_LOGIN_COUNT = 34;
  /**
   * The enumerated value for the getPasswordChangedByRequiredTime operation.
   */
  public static final int OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME = 35;
  /**
   * The enumerated value for the setPasswordChangedByRequiredTime operation.
   */
  public static final int OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME = 36;
  /**
   * The enumerated value for the clearPasswordChangedByRequiredTime operation.
   */
  public static final int OP_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME = 37;
  /**
   * The enumerated value for the getSecondsUntilRequiredChangeTime operation.
   */
  public static final int OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME = 38;
  // The set of attributes to request when retrieving a user's entry.
  private LinkedHashSet<String> requestAttributes;
  // The search filter that will be used to retrieve user entries.
  private SearchFilter userFilter;
  /**
   * Create an instance of this password policy state extended operation.  All
   * initialization should be performed in the
   * {@code initializeExtendedOperationHandler} method.
   */
  public PasswordPolicyStateExtendedOperation()
  {
    super();
  }
  /**
   * Initializes this extended operation handler based on the information in the
   * provided configuration entry.  It should also register itself with the
   * Directory Server for the particular kinds of extended operations that it
   * will process.
   *
   * @param  config       The configuration that contains the information
   *                      to use to initialize this extended operation handler.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem occurs during initialization
   *                                   that is not related to the server
   *                                   configuration.
   */
  public void initializeExtendedOperationHandler(
       ExtendedOperationHandlerCfg config)
       throws ConfigException, InitializationException
  {
    // Construct the filter that will be used to retrieve user entries.
    try
    {
      userFilter = SearchFilter.createFilterFromString("(objectClass=*)");
    }
    catch (Exception e)
    {
      // This should never happen.
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
    }
    // Construct the set of request attributes.
    requestAttributes = new LinkedHashSet<String>(2);
    requestAttributes.add("*");
    requestAttributes.add("+");
    DirectoryServer.registerSupportedExtension(OID_PASSWORD_POLICY_STATE_EXTOP,
                                               this);
  }
  /**
   * Performs any finalization that may be necessary for this extended
   * operation handler.  By default, no finalization is performed.
   */
  public void finalizeExtendedOperationHandler()
  {
    DirectoryServer.deregisterSupportedExtension(OID_CANCEL_REQUEST);
  }
  /**
   * Processes the provided extended operation.
   *
   * @param  operation  The extended operation to be processed.
   */
  public void processExtendedOperation(ExtendedOperation operation)
  {
    operation.setResultCode(ResultCode.UNDEFINED);
    // The user must have the password-reset privilege in order to be able to do
    // anything with this extended operation.
    ClientConnection clientConnection = operation.getClientConnection();
    if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, operation))
    {
      String message = getMessage(MSGID_PWPSTATE_EXTOP_NO_PRIVILEGE);
      operation.appendErrorMessage(message);
      operation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
      return;
    }
    // There must be a request value, and it must be a sequence.  Decode it
    // into its components.
    ASN1OctetString requestValue = operation.getRequestValue();
    if (requestValue == null)
    {
      String message = getMessage(MSGID_PWPSTATE_EXTOP_NO_REQUEST_VALUE);
      operation.appendErrorMessage(message);
      operation.setResultCode(ResultCode.PROTOCOL_ERROR);
      return;
    }
    ASN1OctetString dnString;
    ASN1Sequence    opSequence;
    try
    {
      ASN1Sequence valueSequence =
           ASN1Sequence.decodeAsSequence(requestValue.value());
      List<ASN1Element> elements = valueSequence.elements();
      dnString   = elements.get(0).decodeAsOctetString();
      if (elements.size() == 2)
      {
        opSequence = elements.get(1).decodeAsSequence();
      }
      else
      {
        opSequence = null;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      String message = getMessage(MSGID_PWPSTATE_EXTOP_DECODE_FAILURE,
                                  getExceptionMessage(e));
      operation.appendErrorMessage(message);
      operation.setResultCode(ResultCode.PROTOCOL_ERROR);
      return;
    }
    // Decode the DN and get the corresponding user entry.
    DN targetDN;
    try
    {
      targetDN = DN.decode(dnString);
    }
    catch (DirectoryException de)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, de);
      }
      operation.setResponseData(de);
      return;
    }
    DN rootDN = DirectoryServer.getActualRootBindDN(targetDN);
    if (rootDN != null)
    {
      targetDN = rootDN;
    }
    Entry userEntry;
    InternalClientConnection conn =
         new InternalClientConnection(clientConnection.getAuthenticationInfo());
    InternalSearchOperation internalSearch =
         conn.processSearch(targetDN, SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 1, 0,
                            false, userFilter, requestAttributes, null);
    if (internalSearch.getResultCode() != ResultCode.SUCCESS)
    {
      operation.setResultCode(internalSearch.getResultCode());
      operation.setErrorMessage(internalSearch.getErrorMessage());
      operation.setMatchedDN(internalSearch.getMatchedDN());
      operation.setReferralURLs(internalSearch.getReferralURLs());
      return;
    }
    List<SearchResultEntry> matchingEntries = internalSearch.getSearchEntries();
    if (matchingEntries.isEmpty())
    {
      operation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
      return;
    }
    else if (matchingEntries.size() > 1)
    {
      String message = getMessage(MSGID_PWPSTATE_EXTOP_MULTIPLE_ENTRIES);
      operation.appendErrorMessage(message);
      operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
      return;
    }
    else
    {
      userEntry = matchingEntries.get(0);
    }
    // Get the password policy state for the user entry.
    PasswordPolicyState pwpState;
    PasswordPolicy      policy;
    try
    {
      pwpState = new PasswordPolicyState(userEntry, false, false);
      policy   = pwpState.getPolicy();
    }
    catch (DirectoryException de)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, de);
      }
      operation.setResponseData(de);
      return;
    }
    // Create a hash set that will be used to hold the types of the return
    // types that should be included in the response.
    boolean returnAll;
    LinkedHashSet<Integer> returnTypes = new LinkedHashSet<Integer>();
    if ((opSequence == null) || opSequence.elements().isEmpty())
    {
      returnAll = true;
    }
    else
    {
      returnAll = false;
      for (ASN1Element element : opSequence.elements())
      {
        int opType;
        ArrayList<String> opValues;
        try
        {
          List<ASN1Element> opElements = element.decodeAsSequence().elements();
          opType = opElements.get(0).decodeAsEnumerated().intValue();
          if (opElements.size() == 1)
          {
            opValues = null;
          }
          else
          {
            List<ASN1Element> valueElements =
                 opElements.get(1).decodeAsSequence().elements();
            if (valueElements.isEmpty())
            {
              opValues = null;
            }
            else
            {
              opValues = new ArrayList<String>(valueElements.size());
              for (ASN1Element e : valueElements)
              {
                opValues.add(e.decodeAsOctetString().stringValue());
              }
            }
          }
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
          String message = getMessage(MSGID_PWPSTATE_EXTOP_INVALID_OP_ENCODING);
          operation.appendErrorMessage(message);
          operation.setResultCode(ResultCode.PROTOCOL_ERROR);
          return;
        }
        switch (opType)
        {
          case OP_GET_PASSWORD_POLICY_DN:
            returnTypes.add(OP_GET_PASSWORD_POLICY_DN);
            break;
          case OP_GET_ACCOUNT_DISABLED_STATE:
            returnTypes.add(OP_GET_ACCOUNT_DISABLED_STATE);
            break;
          case OP_SET_ACCOUNT_DISABLED_STATE:
            if (opValues == null)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_NO_DISABLED_VALUE;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_DISABLED_VALUE_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              String value = opValues.get(0);
              if (value.equalsIgnoreCase("true"))
              {
                pwpState.setDisabled(true);
              }
              else if (value.equalsIgnoreCase("false"))
              {
                pwpState.setDisabled(false);
              }
              else
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_DISABLED_VALUE;
                operation.appendErrorMessage(getMessage(msgID));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_ACCOUNT_DISABLED_STATE);
            break;
          case OP_CLEAR_ACCOUNT_DISABLED_STATE:
            pwpState.setDisabled(false);
            returnTypes.add(OP_GET_ACCOUNT_DISABLED_STATE);
            break;
          case OP_GET_ACCOUNT_EXPIRATION_TIME:
            returnTypes.add(OP_GET_ACCOUNT_EXPIRATION_TIME);
            break;
          case OP_SET_ACCOUNT_EXPIRATION_TIME:
            if (opValues == null)
            {
              pwpState.setAccountExpirationTime(pwpState.getCurrentTime());
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                pwpState.setAccountExpirationTime(time);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE;
                operation.appendErrorMessage(getMessage(msgID, opValues.get(0),
                                                        de.getErrorMessage()));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_ACCOUNT_EXPIRATION_TIME);
            break;
          case OP_CLEAR_ACCOUNT_EXPIRATION_TIME:
            pwpState.clearAccountExpirationTime();
            returnTypes.add(OP_GET_ACCOUNT_EXPIRATION_TIME);
            break;
          case OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION:
            returnTypes.add(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION);
            break;
          case OP_GET_PASSWORD_CHANGED_TIME:
            returnTypes.add(OP_GET_PASSWORD_CHANGED_TIME);
            break;
          case OP_SET_PASSWORD_CHANGED_TIME:
            if (opValues == null)
            {
              pwpState.setPasswordChangedTime();
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                pwpState.setPasswordChangedTime(time);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE;
                operation.appendErrorMessage(getMessage(msgID, opValues.get(0),
                                                        de.getErrorMessage()));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_PASSWORD_CHANGED_TIME);
            break;
          case OP_CLEAR_PASSWORD_CHANGED_TIME:
            pwpState.clearPasswordChangedTime();
            returnTypes.add(OP_GET_PASSWORD_CHANGED_TIME);
            break;
          case OP_GET_PASSWORD_EXPIRATION_WARNED_TIME:
            returnTypes.add(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME);
            break;
          case OP_SET_PASSWORD_EXPIRATION_WARNED_TIME:
            if (opValues == null)
            {
              pwpState.setWarnedTime();
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                pwpState.setWarnedTime(time);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE;
                operation.appendErrorMessage(getMessage(msgID,
                                                        opValues.get(0),
                                                        de.getErrorMessage()));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME);
            break;
          case OP_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME:
            pwpState.clearWarnedTime();
            returnTypes.add(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME);
            break;
          case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION:
            returnTypes.add(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION);
            break;
          case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING:
            returnTypes.add(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING);
            break;
          case OP_GET_AUTHENTICATION_FAILURE_TIMES:
            returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
            break;
          case OP_ADD_AUTHENTICATION_FAILURE_TIME:
            if (opValues == null)
            {
              if (policy.getLockoutFailureCount() == 0)
              {
                returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
                break;
              }
              pwpState.updateAuthFailureTimes();
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_ADD_FAILURE_TIME_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                List<Long> authFailureTimes = pwpState.getAuthFailureTimes();
                ArrayList<Long> newFailureTimes =
                     new ArrayList<Long>(authFailureTimes.size()+1);
                newFailureTimes.addAll(authFailureTimes);
                newFailureTimes.add(time);
                pwpState.setAuthFailureTimes(newFailureTimes);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_AUTH_FAILURE_TIME;
                String message = getMessage(msgID, opValues.get(0),
                                            de.getErrorMessage());
                operation.setResultCode(de.getResultCode());
                operation.appendErrorMessage(message);
                return;
              }
            }
            returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
            break;
          case OP_SET_AUTHENTICATION_FAILURE_TIMES:
            if (opValues == null)
            {
              ArrayList<Long> valueList = new ArrayList<Long>(1);
              valueList.add(pwpState.getCurrentTime());
              pwpState.setAuthFailureTimes(valueList);
            }
            else
            {
              ArrayList<Long> valueList = new ArrayList<Long>(opValues.size());
              for (String s : opValues)
              {
                try
                {
                  valueList.add(
                       GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                            new ASN1OctetString(s)));
                }
                catch (DirectoryException de)
                {
                  int msgID = MSGID_PWPSTATE_EXTOP_BAD_AUTH_FAILURE_TIME;
                  String message = getMessage(msgID, s, de.getErrorMessage());
                  operation.setResultCode(de.getResultCode());
                  operation.appendErrorMessage(message);
                  return;
                }
              }
              pwpState.setAuthFailureTimes(valueList);
            }
            returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
            break;
          case OP_CLEAR_AUTHENTICATION_FAILURE_TIMES:
            pwpState.clearFailureLockout();
            returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
            break;
          case OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK:
            returnTypes.add(OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK);
            break;
          case OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT:
            returnTypes.add(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT);
            break;
          case OP_GET_LAST_LOGIN_TIME:
            returnTypes.add(OP_GET_LAST_LOGIN_TIME);
            break;
          case OP_SET_LAST_LOGIN_TIME:
            if (opValues == null)
            {
              pwpState.setLastLoginTime();
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                pwpState.setLastLoginTime(time);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME;
                operation.appendErrorMessage(getMessage(msgID, opValues.get(0),
                                                        de.getErrorMessage()));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_LAST_LOGIN_TIME);
            break;
          case OP_CLEAR_LAST_LOGIN_TIME:
            pwpState.clearLastLoginTime();
            returnTypes.add(OP_GET_LAST_LOGIN_TIME);
            break;
          case OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT:
            returnTypes.add(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT);
            break;
          case OP_GET_PASSWORD_RESET_STATE:
            returnTypes.add(OP_GET_PASSWORD_RESET_STATE);
            break;
          case OP_SET_PASSWORD_RESET_STATE:
            if (opValues == null)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_NO_RESET_STATE_VALUE;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              String value = opValues.get(0);
              if (value.equalsIgnoreCase("true"))
              {
                pwpState.setMustChangePassword(true);
              }
              else if (value.equalsIgnoreCase("false"))
              {
                pwpState.setMustChangePassword(false);
              }
              else
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE;
                operation.appendErrorMessage(getMessage(msgID));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_PASSWORD_RESET_STATE);
            break;
          case OP_CLEAR_PASSWORD_RESET_STATE:
            pwpState.setMustChangePassword(false);
            returnTypes.add(OP_GET_PASSWORD_RESET_STATE);
            break;
          case OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT:
            returnTypes.add(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT);
            break;
          case OP_GET_GRACE_LOGIN_USE_TIMES:
            returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
            break;
          case OP_ADD_GRACE_LOGIN_USE_TIME:
            if (opValues == null)
            {
              pwpState.updateGraceLoginTimes();
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_ADD_GRACE_LOGIN_TIME_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                List<Long> authFailureTimes = pwpState.getGraceLoginTimes();
                ArrayList<Long> newGraceTimes =
                     new ArrayList<Long>(authFailureTimes.size()+1);
                newGraceTimes.addAll(authFailureTimes);
                newGraceTimes.add(time);
                pwpState.setGraceLoginTimes(newGraceTimes);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_GRACE_LOGIN_TIME;
                String message = getMessage(msgID, opValues.get(0),
                                            de.getErrorMessage());
                operation.setResultCode(de.getResultCode());
                operation.appendErrorMessage(message);
                return;
              }
            }
            returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
            break;
          case OP_SET_GRACE_LOGIN_USE_TIMES:
            if (opValues == null)
            {
              ArrayList<Long> valueList = new ArrayList<Long>(1);
              valueList.add(pwpState.getCurrentTime());
              pwpState.setGraceLoginTimes(valueList);
            }
            else
            {
              ArrayList<Long> valueList = new ArrayList<Long>(opValues.size());
              for (String s : opValues)
              {
                try
                {
                  valueList.add(
                       GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                            new ASN1OctetString(s)));
                }
                catch (DirectoryException de)
                {
                  int msgID = MSGID_PWPSTATE_EXTOP_BAD_GRACE_LOGIN_TIME;
                  String message = getMessage(msgID, s, de.getErrorMessage());
                  operation.setResultCode(de.getResultCode());
                  operation.appendErrorMessage(message);
                  return;
                }
              }
              pwpState.setGraceLoginTimes(valueList);
            }
            returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
            break;
          case OP_CLEAR_GRACE_LOGIN_USE_TIMES:
            pwpState.clearGraceLoginTimes();
            returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
            break;
          case OP_GET_REMAINING_GRACE_LOGIN_COUNT:
            returnTypes.add(OP_GET_REMAINING_GRACE_LOGIN_COUNT);
            break;
          case OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME:
            returnTypes.add(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
            break;
          case OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME:
            if (opValues == null)
            {
              pwpState.setRequiredChangeTime();
            }
            else if (opValues.size() != 1)
            {
              int msgID = MSGID_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME_COUNT;
              operation.appendErrorMessage(getMessage(msgID));
              operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
              return;
            }
            else
            {
              try
              {
                ASN1OctetString valueString =
                     new ASN1OctetString(opValues.get(0));
                long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
                                 valueString);
                pwpState.setRequiredChangeTime(time);
              }
              catch (DirectoryException de)
              {
                int msgID = MSGID_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME;
                operation.appendErrorMessage(getMessage(msgID, opValues.get(0),
                                                        de.getErrorMessage()));
                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
                return;
              }
            }
            returnTypes.add(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
            break;
          case OP_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME:
            pwpState.clearRequiredChangeTime();
            returnTypes.add(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
            break;
          case OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME:
            returnTypes.add(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME);
            break;
          default:
            int msgID = MSGID_PWPSTATE_EXTOP_UNKNOWN_OP_TYPE;
            operation.appendErrorMessage(getMessage(msgID, opType));
            operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
            return;
        }
      }
      // If there are any modifications that need to be made to the password
      // policy state, then apply them now.
      List<Modification> stateMods = pwpState.getModifications();
      if ((stateMods != null) && (! stateMods.isEmpty()))
      {
        ModifyOperation modifyOperation =
             conn.processModify(targetDN, stateMods);
        if (modifyOperation.getResultCode() != ResultCode.SUCCESS)
        {
          operation.setResultCode(modifyOperation.getResultCode());
          operation.setErrorMessage(modifyOperation.getErrorMessage());
          operation.setMatchedDN(modifyOperation.getMatchedDN());
          operation.setReferralURLs(modifyOperation.getReferralURLs());
          return;
        }
      }
    }
    // Construct the sequence of values to return.
    ArrayList<ASN1Element> opElements = new ArrayList<ASN1Element>();
    if (returnAll || returnTypes.contains(OP_GET_PASSWORD_POLICY_DN))
    {
      opElements.add(encode(OP_GET_PASSWORD_POLICY_DN,
                            policy.getConfigEntryDN().toString()));
    }
    if (returnAll || returnTypes.contains(OP_GET_ACCOUNT_DISABLED_STATE))
    {
      opElements.add(encode(OP_GET_ACCOUNT_DISABLED_STATE,
                            String.valueOf(pwpState.isDisabled())));
    }
    if (returnAll || returnTypes.contains(OP_GET_ACCOUNT_EXPIRATION_TIME))
    {
      String expTimeStr;
      long expTime = pwpState.getAccountExpirationTime();
      if (expTime < 0)
      {
        expTimeStr = null;
      }
      else
      {
        expTimeStr = GeneralizedTimeSyntax.format(expTime);
      }
      opElements.add(encode(OP_GET_ACCOUNT_EXPIRATION_TIME, expTimeStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION))
    {
      String secondsStr;
      long expTime = pwpState.getAccountExpirationTime();
      if (expTime < 0)
      {
        secondsStr = null;
      }
      else
      {
        secondsStr =
             String.valueOf((expTime - pwpState.getCurrentTime()) / 1000);
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
                            secondsStr));
    }
    if (returnAll || returnTypes.contains(OP_GET_PASSWORD_CHANGED_TIME))
    {
      String timeStr;
      long changedTime = pwpState.getPasswordChangedTime();
      if (changedTime < 0)
      {
        timeStr = null;
      }
      else
      {
        timeStr = GeneralizedTimeSyntax.format(changedTime);
      }
      opElements.add(encode(OP_GET_PASSWORD_CHANGED_TIME, timeStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME))
    {
      String timeStr;
      long warnedTime = pwpState.getWarnedTime();
      if (warnedTime < 0)
      {
        timeStr = null;
      }
      else
      {
        timeStr = GeneralizedTimeSyntax.format(warnedTime);
      }
      opElements.add(encode(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME, timeStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION))
    {
      String secondsStr;
      int secondsUntilExp = pwpState.getSecondsUntilExpiration();
      if (secondsUntilExp < 0)
      {
        secondsStr = null;
      }
      else
      {
        secondsStr = String.valueOf(secondsUntilExp);
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
                            secondsStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING))
    {
      String secondsStr;
      int secondsUntilExp = pwpState.getSecondsUntilExpiration();
      if (secondsUntilExp < 0)
      {
        secondsStr = null;
      }
      else
      {
        int secondsUntilWarning = secondsUntilExp - policy.getWarningInterval();
        if (secondsUntilWarning <= 0)
        {
          secondsStr = "0";
        }
        else
        {
          secondsStr = String.valueOf(secondsUntilWarning);
        }
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
                            secondsStr));
    }
    if (returnAll || returnTypes.contains(OP_GET_AUTHENTICATION_FAILURE_TIMES))
    {
      opElements.add(encode(OP_GET_AUTHENTICATION_FAILURE_TIMES,
                            pwpState.getAuthFailureTimes()));
    }
    if (returnAll || returnTypes.contains(
                          OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK))
    {
      // We have to check whether the account is locked due to failures before
      // we can get the length of time until the account is unlocked.
      String secondsStr;
      if (pwpState.lockedDueToFailures())
      {
        int seconds = pwpState.getSecondsUntilUnlock();
        if (seconds <= 0)
        {
          secondsStr = null;
        }
        else
        {
          secondsStr = String.valueOf(seconds);
        }
      }
      else
      {
        secondsStr = null;
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
                            secondsStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT))
    {
      String remainingFailuresStr;
      int allowedFailureCount = policy.getLockoutFailureCount();
      if (allowedFailureCount > 0)
      {
        int remainingFailures =
                 allowedFailureCount - pwpState.getAuthFailureTimes().size();
        if (remainingFailures < 0)
        {
          remainingFailures = 0;
        }
        remainingFailuresStr = String.valueOf(remainingFailures);
      }
      else
      {
        remainingFailuresStr = null;
      }
      opElements.add(encode(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
                            remainingFailuresStr));
    }
    if (returnAll || returnTypes.contains(OP_GET_LAST_LOGIN_TIME))
    {
      String timeStr;
      long lastLoginTime = pwpState.getLastLoginTime();
      if (lastLoginTime < 0)
      {
        timeStr = null;
      }
      else
      {
        timeStr = GeneralizedTimeSyntax.format(lastLoginTime);
      }
      opElements.add(encode(OP_GET_LAST_LOGIN_TIME, timeStr));
    }
    if (returnAll || returnTypes.contains(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT))
    {
      String secondsStr;
      int lockoutInterval = policy.getIdleLockoutInterval();
      if (lockoutInterval > 0)
      {
        long lastLoginTime = pwpState.getLastLoginTime();
        if (lastLoginTime < 0)
        {
          secondsStr = "0";
        }
        else
        {
          long lockoutTime = lastLoginTime + (lockoutInterval*1000);
          long currentTime = pwpState.getCurrentTime();
          int secondsUntilLockout = (int) ((lockoutTime - currentTime) / 1000);
          if (secondsUntilLockout <= 0)
          {
            secondsStr = "0";
          }
          else
          {
            secondsStr = String.valueOf(secondsUntilLockout);
          }
        }
      }
      else
      {
        secondsStr = null;
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT, secondsStr));
    }
    if (returnAll || returnTypes.contains(OP_GET_PASSWORD_RESET_STATE))
    {
      opElements.add(encode(OP_GET_PASSWORD_RESET_STATE,
                            String.valueOf(pwpState.mustChangePassword())));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT))
    {
      String secondsStr;
      if (pwpState.mustChangePassword())
      {
        int maxAge = policy.getMaximumPasswordResetAge();
        if (maxAge > 0)
        {
          long currentTime = pwpState.getCurrentTime();
          long changedTime = pwpState.getPasswordChangedTime();
          int changeAge = (int) ((currentTime - changedTime) / 1000);
          int timeToLockout = maxAge - changeAge;
          if (timeToLockout <= 0)
          {
            secondsStr = "0";
          }
          else
          {
            secondsStr = String.valueOf(timeToLockout);
          }
        }
        else
        {
          secondsStr = null;
        }
      }
      else
      {
        secondsStr = null;
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
                            secondsStr));
    }
    if (returnAll || returnTypes.contains(OP_GET_GRACE_LOGIN_USE_TIMES))
    {
      opElements.add(encode(OP_GET_GRACE_LOGIN_USE_TIMES,
                            pwpState.getGraceLoginTimes()));
    }
    if (returnAll || returnTypes.contains(OP_GET_REMAINING_GRACE_LOGIN_COUNT))
    {
      String remainingStr;
      int remainingGraceLogins = pwpState.getGraceLoginsRemaining();
      if (remainingGraceLogins <= 0)
      {
        remainingStr = "0";
      }
      else
      {
        remainingStr = String.valueOf(remainingGraceLogins);
      }
      opElements.add(encode(OP_GET_REMAINING_GRACE_LOGIN_COUNT, remainingStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME))
    {
      String timeStr;
      long requiredChangeTime = pwpState.getRequiredChangeTime();
      if (requiredChangeTime < 0)
      {
        timeStr = null;
      }
      else
      {
        timeStr = GeneralizedTimeSyntax.format(requiredChangeTime);
      }
      opElements.add(encode(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME, timeStr));
    }
    if (returnAll ||
        returnTypes.contains(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME))
    {
      String secondsStr;
      long policyRequiredChangeTime = policy.getRequireChangeByTime();
      if (policyRequiredChangeTime > 0)
      {
        long accountRequiredChangeTime = pwpState.getRequiredChangeTime();
        if (accountRequiredChangeTime >= policyRequiredChangeTime)
        {
          secondsStr = null;
        }
        else
        {
          long currentTime = pwpState.getCurrentTime();
          if (currentTime >= policyRequiredChangeTime)
          {
            secondsStr = "0";
          }
          else
          {
            secondsStr =
                 String.valueOf((policyRequiredChangeTime-currentTime) / 1000);
          }
        }
      }
      else
      {
        secondsStr = null;
      }
      opElements.add(encode(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
                            secondsStr));
    }
    ArrayList<ASN1Element> responseValueElements =
         new ArrayList<ASN1Element>(2);
    responseValueElements.add(dnString);
    responseValueElements.add(new ASN1Sequence(opElements));
    ASN1OctetString responseValue =
         new ASN1OctetString(new ASN1Sequence(responseValueElements).encode());
    operation.setResponseOID(OID_PASSWORD_POLICY_STATE_EXTOP);
    operation.setResponseValue(responseValue);
    operation.setResultCode(ResultCode.SUCCESS);
  }
  /**
   * Encodes the provided information in a form suitable for including in the
   * response value.
   *
   * @param  opType  The operation type to use for the value.
   * @param  value   The single value to include in the response.
   *
   * @return  The encoded ASN.1 element.
   */
  public static ASN1Element encode(int opType, String value)
  {
    ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
    elements.add(new ASN1Enumerated(opType));
    if (value != null)
    {
      ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(1);
      valueElements.add(new ASN1OctetString(value));
      elements.add(new ASN1Sequence(valueElements));
    }
    return new ASN1Sequence(elements);
  }
  /**
   * Encodes the provided information in a form suitable for including in the
   * response value.
   *
   * @param  opType  The operation type to use for the value.
   * @param  values  The set of string values to include in the response.
   *
   * @return  The encoded ASN.1 element.
   */
  public static ASN1Element encode(int opType, String[] values)
  {
    ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
    elements.add(new ASN1Enumerated(opType));
    if ((values != null) && (values.length > 0))
    {
      ArrayList<ASN1Element> valueElements =
           new ArrayList<ASN1Element>(values.length);
      for (int i=0; i < values.length; i++)
      {
        valueElements.add(new ASN1OctetString(values[i]));
      }
      elements.add(new ASN1Sequence(valueElements));
    }
    return new ASN1Sequence(elements);
  }
  /**
   * Encodes the provided information in a form suitable for including in the
   * response value.
   *
   * @param  opType  The operation type to use for the value.
   * @param  values  The set of timestamp values to include in the response.
   *
   * @return  The encoded ASN.1 element.
   */
  public static ASN1Element encode(int opType, List<Long> values)
  {
    ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
    elements.add(new ASN1Enumerated(opType));
    ArrayList<ASN1Element> valueElements =
         new ArrayList<ASN1Element>(values.size());
    for (long l : values)
    {
      valueElements.add(new ASN1OctetString(GeneralizedTimeSyntax.format(l)));
    }
    elements.add(new ASN1Sequence(valueElements));
    return new ASN1Sequence(elements);
  }
}
opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -5212,6 +5212,274 @@
  /**
   * The message ID for the message that will be used if a user requests the
   * password policy state extended operation but does not have adequate
   * privileges to do so.  This does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_NO_PRIVILEGE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 502;
  /**
   * The message ID for the message that will be used if a user requests the
   * password policy state extended operation but does not include a request
   * value.  This does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_NO_REQUEST_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 503;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to decode the password policy state extended request value.
   * This takes a single argument, which is a message explaining the problem
   * that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_DECODE_FAILURE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 504;
  /**
   * The message ID for the message that will be used if the server finds
   * multiple entries for a given DN.  This takes a single argument, which is a
   * string representation of the provided DN.
   */
  public static final int MSGID_PWPSTATE_EXTOP_MULTIPLE_ENTRIES =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 505;
  /**
   * The message ID for the message that will be used if the password policy
   * state request includes an operation with an invalid encoding.  This takes a
   * single argument, which is a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_INVALID_OP_ENCODING =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 506;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the disabled state for an account but does not provide a value.  This
   * does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_NO_DISABLED_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 507;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the disabled state for an account but provides multiple values.  This
   * does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_DISABLED_VALUE_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 508;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the disabled state for an account but provides an invalid value.  This
   * does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_DISABLED_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 509;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the account expiration time but provides multiple values.  This does
   * not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 510;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the account expiration time but provides an invalid value.  This takes
   * two arguments, which are the provided value and a message explaining the
   * problem that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 511;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the password changed time but provides multiple values.  This does not
   * take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 512;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the password changed time but provides an invalid value.  This takes
   * two arguments, which are the provided value and a message explaining the
   * problem that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 513;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the password warned time but provides multiple values.  This does not
   * take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 514;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the password warned time but provides an invalid value.  This takes
   * two arguments, which are the provided value and a message explaining the
   * problem that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 515;
  /**
   * The message ID for the message that will be used if the user attempts to
   * add a failure time but provides multiple values.  This does not take any
   * arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_ADD_FAILURE_TIME_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 516;
  /**
   * The message ID for the message that will be used if the user attempts to
   * alter the failure times but provides an invalid value.  This takes two
   * arguments, which are the provided value and a message explaining the
   * that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_AUTH_FAILURE_TIME =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 517;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the last login time but provides multiple values.  This does not take
   * any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 518;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the last login time but provides an invalid value.  This takes two
   * arguments, which are the provided value and a message explaining the
   * problem that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 519;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the reset state for an account but does not provide a value.  This
   * does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_NO_RESET_STATE_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 520;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the reset state for an account but provides multiple values.  This does
   * does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 521;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the reset state for an account but provides an invalid value.  This
   * does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 522;
  /**
   * The message ID for the message that will be used if the user attempts to
   * add a grace login time but provides multiple values.  This does not take
   * any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_ADD_GRACE_LOGIN_TIME_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 523;
  /**
   * The message ID for the message that will be used if the user attempts to
   * alter the grace login times but provides an invalid value.  This takes two
   * arguments, which are the provided value and a message explaining the
   * that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_GRACE_LOGIN_TIME =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 524;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the required change time but provides multiple values.  This does not
   * take any arguments.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME_COUNT =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 525;
  /**
   * The message ID for the message that will be used if the user attempts to
   * set the required change time but provides an invalid value.  This takes two
   * arguments, which are the provided value and a message explaining the
   * problem that occurred.
   */
  public static final int MSGID_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 526;
  /**
   * The message ID for the message that will be used if the password policy
   * state request includes an operation with an unrecognized type.  This takes
   * a single argument, which is the provided operation type.
   */
  public static final int MSGID_PWPSTATE_EXTOP_UNKNOWN_OP_TYPE =
       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_SEVERE_ERROR | 527;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -7492,6 +7760,121 @@
    registerMessage(MSGID_ENTRYUUID_VATTR_NOT_SEARCHABLE,
                    "The %s attribute is not searchable and should not be " +
                    "included in otherwise unindexed search filters");
    registerMessage(MSGID_PWPSTATE_EXTOP_NO_PRIVILEGE,
                    "You do not have sufficient privileges to use the " +
                    "password policy state extended operation");
    registerMessage(MSGID_PWPSTATE_EXTOP_NO_REQUEST_VALUE,
                    "The provided password policy state extended request " +
                    "did not include a request value");
    registerMessage(MSGID_PWPSTATE_EXTOP_DECODE_FAILURE,
                    "An unexpected error occurred while attempting to decode " +
                    "password policy state extended request value:  %s");
    registerMessage(MSGID_PWPSTATE_EXTOP_MULTIPLE_ENTRIES,
                    "SEVERE ERROR:  Multiple entries were found with DN %s");
    registerMessage(MSGID_PWPSTATE_EXTOP_INVALID_OP_ENCODING,
                    "An unexpected error occurred while attempting to decode " +
                    "an operation from the password policy state extended " +
                    "request:  %s");
    registerMessage(MSGID_PWPSTATE_EXTOP_NO_DISABLED_VALUE,
                    "No value was provided for the password policy state " +
                    "operation intended to set the disabled state for the " +
                    "user.  Exactly one value (either 'true' or 'false') " +
                    "must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_DISABLED_VALUE_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the disabled state " +
                    "for the user.  Exactly one value (either 'true' or " +
                    "'false') must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_DISABLED_VALUE,
                    "The value provided for the password policy state " +
                    "operation  intended to set the disabled state for the " +
                    "user was invalid.  The value must be either 'true' or " +
                    "'false'");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the account expiration " +
                    "time for the user.  Exactly one value must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE,
                    "The value %s provided for the password policy state " +
                    "operation used to set the account expiration time was " +
                    "invalid:  %s.  The value should be specifed using the " +
                    "generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the password changed " +
                    "time for the user.  Exactly one value must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE,
                    "The value %s provided for the password policy state " +
                    "operation used to set the password changed time was " +
                    "invalid:  %s.  The value should be specifed using the " +
                    "generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the password warned " +
                    "time for the user.  Exactly one value must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE,
                    "The value %s provided for the password policy state " +
                    "operation used to set the password warned time was " +
                    "invalid:  %s.  The value should be specifed using the " +
                    "generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_ADD_FAILURE_TIME_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to add an authentication " +
                    "failure time for the user.  Exactly one value must be " +
                    "given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_AUTH_FAILURE_TIME,
                    "The value %s provided for the password policy state " +
                    "operation used to update the authentication failure " +
                    "times was invalid:  %s.  The value should be specifed " +
                    "using the generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the last login time for " +
                    "the user.  Exactly one value must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME,
                    "The value %s provided for the password policy state " +
                    "operation used to set the last login time was invalid:  " +
                    "%s.  The value should be specifed using the " +
                    "generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_NO_RESET_STATE_VALUE,
                    "No value was provided for the password policy state " +
                    "operation intended to set the reset state for the " +
                    "user.  Exactly one value (either 'true' or 'false') " +
                    "must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the reset state " +
                    "for the user.  Exactly one value (either 'true' or " +
                    "'false') must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE,
                    "The value provided for the password policy state " +
                    "operation  intended to set the reset state for the " +
                    "user was invalid.  The value must be either 'true' or " +
                    "'false'");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_ADD_GRACE_LOGIN_TIME_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to add a grace login use time " +
                    "for the user.  Exactly one value must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_GRACE_LOGIN_TIME,
                    "The value %s provided for the password policy state " +
                    "operation used to update the grace login use times was " +
                    "invalid:  %s.  The value should be specifed using the " +
                    "generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME_COUNT,
                    "Multiple values were provided for the password policy " +
                    "state operation intended to set the required change " +
                    "time for the user.  Exactly one value must be given");
    registerMessage(MSGID_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME,
                    "The value %s provided for the password policy state " +
                    "operation used to set the required change time was " +
                    "invalid:  %s.  The value should be specifed using the " +
                    "generalized time format");
    registerMessage(MSGID_PWPSTATE_EXTOP_UNKNOWN_OP_TYPE,
                    "The password policy state extended request included an " +
                    "operation with an invalid or unsupported operation type " +
                    "of %s");
  }
}
opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -8028,6 +8028,918 @@
  /**
   * The message ID for the message that will be used as the description for the
   * password policy state tool.
   */
  public static final int MSGID_PWPSTATE_TOOL_DESCRIPTION =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1094;
  /**
   * The message ID for the message that will be used as the description for the
   * hostname argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_HOST =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1095;
  /**
   * The message ID for the message that will be used as the description for the
   * port argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_PORT =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1096;
  /**
   * The message ID for the message that will be used as the description for the
   * useSSL argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_USESSL =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1097;
  /**
   * The message ID for the message that will be used as the description for the
   * useStartTLS argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_USESTARTTLS =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1098;
  /**
   * The message ID for the message that will be used as the description for the
   * bindDN argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_BINDDN =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1099;
  /**
   * The message ID for the message that will be used as the description for the
   * bindPassword argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_BINDPW =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1100;
  /**
   * The message ID for the message that will be used as the description for the
   * bindPasswordFile argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_BINDPWFILE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1101;
  /**
   * The message ID for the message that will be used as the description for the
   * targetDN argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_TARGETDN =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1102;
  /**
   * The message ID for the message that will be used as the description for the
   * saslOptions argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_SASLOPTIONS =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1103;
  /**
   * The message ID for the message that will be used as the description for the
   * trustAll argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_TRUST_ALL =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1104;
  /**
   * The message ID for the message that will be used as the description for the
   * keyStorePath argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_KSFILE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1105;
  /**
   * The message ID for the message that will be used as the description for the
   * keyStorePIN argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_KSPW =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1106;
  /**
   * The message ID for the message that will be used as the description for the
   * keyStorePINFile argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_KSPWFILE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1107;
  /**
   * The message ID for the message that will be used as the description for the
   * trustStorePath argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_TSFILE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1108;
  /**
   * The message ID for the message that will be used as the description for the
   * trustStorePIN argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_TSPW =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1109;
  /**
   * The message ID for the message that will be used as the description for the
   * trustStorePINFile argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_TSPWFILE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1110;
  /**
   * The message ID for the message that will be used as the description for the
   * showUsage argument.
   */
  public static final int MSGID_PWPSTATE_DESCRIPTION_SHOWUSAGE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1111;
  /**
   * The message ID for the message that will be used as the description for the
   * get-all subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_GET_ALL =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1112;
  /**
   * The message ID for the message that will be used as the description for the
   * get-password-policy-dn subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_POLICY_DN =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1113;
  /**
   * The message ID for the message that will be used as the description for the
   * get-account-is-disabled subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_ACCOUNT_DISABLED_STATE =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1114;
  /**
   * The message ID for the message that will be used as the description for the
   * set-account-is-disabled subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_SET_ACCOUNT_DISABLED_STATE =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1115;
  /**
   * The message ID for the message that will be used as the description for the
   * argument used to set Boolean values.
   */
  public static final int MSGID_DESCRIPTION_OPERATION_BOOLEAN_VALUE =
    CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1116;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-account-is-disabled subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_DISABLED_STATE =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1117;
  /**
   * The message ID for the message that will be used as the description for the
   * get-account-expiration-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_ACCOUNT_EXPIRATION_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1118;
  /**
   * The message ID for the message that will be used as the description for the
   * set-account-expiration-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_SET_ACCOUNT_EXPIRATION_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1119;
  /**
   * The message ID for the message that will be used as the description for the
   * argument used to set single values for single-valued attributes.
   */
  public static final int MSGID_DESCRIPTION_OPERATION_TIME_VALUE =
    CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1120;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-account-expiration-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_EXPIRATION_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1121;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-account-expiration subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1122;
  /**
   * The message ID for the message that will be used as the description for the
   * get-password-changed-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_CHANGED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1123;
  /**
   * The message ID for the message that will be used as the description for the
   * set-password-changed-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_CHANGED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1124;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-password-changed-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_CHANGED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1125;
  /**
   * The message ID for the message that will be used as the description for the
   * get-password-expiration-warned-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_EXPIRATION_WARNED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1126;
  /**
   * The message ID for the message that will be used as the description for the
   * set-password-expiration-warned-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_EXPIRATION_WARNED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1127;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-password-expiration-warned-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1128;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-password-expiration subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1129;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-password-expiration-warning subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP_WARNING =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1130;
  /**
   * The message ID for the message that will be used as the description for the
   * get-auth-failure-times subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_GET_AUTH_FAILURE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1131;
  /**
   * The message ID for the message that will be used as the description for the
   * add-auth-failure-time subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_ADD_AUTH_FAILURE_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1132;
  /**
   * The message ID for the message that will be used as the description for the
   * set-auth-failure-times subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_SET_AUTH_FAILURE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1133;
  /**
   * The message ID for the message that will be used as the description for the
   * argument used to set single values for multi-valued attributes.
   */
  public static final int MSGID_DESCRIPTION_OPERATION_TIME_VALUES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1134;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-auth-failure-times subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_CLEAR_AUTH_FAILURE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1135;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-authentication-failure-unlock subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1136;
  /**
   * The message ID for the message that will be used as the description for the
   * get-remaining-authentication-failure-count subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_REMAINING_AUTH_FAILURE_COUNT =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1137;
  /**
   * The message ID for the message that will be used as the description for the
   * get-last-login-time subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_GET_LAST_LOGIN_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1138;
  /**
   * The message ID for the message that will be used as the description for the
   * set-last-login-time subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_SET_LAST_LOGIN_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1139;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-last-login-time subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_CLEAR_LAST_LOGIN_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1140;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-idle-lockout subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_IDLE_LOCKOUT =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1141;
  /**
   * The message ID for the message that will be used as the description for the
   * get-password-is-reset subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_RESET_STATE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1142;
  /**
   * The message ID for the message that will be used as the description for the
   * set-password-is-reset subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_RESET_STATE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1143;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-password-is-reset subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_RESET_STATE =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1144;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-reset-lockout subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_RESET_LOCKOUT =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1145;
  /**
   * The message ID for the message that will be used as the description for the
   * get-grace-login-use-times subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_GET_GRACE_LOGIN_USE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1146;
  /**
   * The message ID for the message that will be used as the description for the
   * add-grace-login-use-time subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_ADD_GRACE_LOGIN_USE_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1147;
  /**
   * The message ID for the message that will be used as the description for the
   * set-grace-login-use-times subcommand.
   */
  public static final int MSGID_DESCRIPTION_PWPSTATE_SET_GRACE_LOGIN_USE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1148;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-grace-login-use-times subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_GRACE_LOGIN_USE_TIMES =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1149;
  /**
   * The message ID for the message that will be used as the description for the
   * get-remaining-grace-login-count subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_REMAINING_GRACE_LOGIN_COUNT =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1150;
  /**
   * The message ID for the message that will be used as the description for the
   * get-password-changed-by-required-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_PW_CHANGED_BY_REQUIRED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1151;
  /**
   * The message ID for the message that will be used as the description for the
   * set-password-changed-by-required-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_SET_PW_CHANGED_BY_REQUIRED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1152;
  /**
   * The message ID for the message that will be used as the description for the
   * clear-password-changed-by-required-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_CLEAR_PW_CHANGED_BY_REQUIRED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1153;
  /**
   * The message ID for the message that will be used as the description for the
   * get-seconds-until-required-time subcommand.
   */
  public static final int
       MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1154;
  /**
   * The message ID for the message that will be used if no subcommand was
   * provided on the command line.  This does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_NO_SUBCOMMAND =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1155;
  /**
   * The message ID for the message that will be used if the operation value
   * provided could not be parsed as a Boolean value.  This takes a single
   * argument, which is the provided value.
   */
  public static final int MSGID_PWPSTATE_INVALID_BOOLEAN_VALUE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1156;
  /**
   * The message ID for the message that will be used if no value was provided
   * for an operation that requires a Boolean value.  This does not take any
   * arguments.
   */
  public static final int MSGID_PWPSTATE_NO_BOOLEAN_VALUE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1157;
  /**
   * The message ID for the message that will be used if an unrecognized
   * subcommand was provided.  This takes a single argument, which is the
   * unrecognized subcommand.
   */
  public static final int MSGID_PWPSTATE_INVALID_SUBCOMMAND =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1158;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to send the extended request to the server.  This takes a single
   * argument, which is a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_CANNOT_SEND_REQUEST_EXTOP =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1159;
  /**
   * The message ID for the message that will be used if the server closed the
   * connection without sending a response.  This does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_CONNECTION_CLOSED_READING_RESPONSE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1160;
  /**
   * The message ID for the message that will be used if the response from the
   * server indicates that the request was not processed properly.  This takes
   * three arguments, which are the result code, a string representation of the
   * result code, and the error message.
   */
  public static final int MSGID_PWPSTATE_REQUEST_FAILED =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1161;
  /**
   * The message ID for the message that will be used if an error occurred while
   * attempting to decode the response message from the server.  This takes a
   * single argument, which is a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_CANNOT_DECODE_RESPONSE_MESSAGE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1162;
  /**
   * The message ID for the message that will be used if an error occurred while
   * attempting to decode a response op.  This takes a single argument, which is
   * a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_CANNOT_DECODE_RESPONSE_OP =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1163;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the password policy DN.
   */
  public static final int MSGID_PWPSTATE_LABEL_PASSWORD_POLICY_DN =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1164;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the account disabled state.
   */
  public static final int MSGID_PWPSTATE_LABEL_ACCOUNT_DISABLED_STATE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1165;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the account expiration time.
   */
  public static final int MSGID_PWPSTATE_LABEL_ACCOUNT_EXPIRATION_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1166;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until account expiration.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_ACCOUNT_EXPIRATION =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1167;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the password changed time.
   */
  public static final int MSGID_PWPSTATE_LABEL_PASSWORD_CHANGED_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1168;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the password expiration warned time.
   */
  public static final int MSGID_PWPSTATE_LABEL_PASSWORD_EXPIRATION_WARNED_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1169;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until password expiration.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1170;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until the password expiration warning.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1171;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the auth failure times.
   */
  public static final int MSGID_PWPSTATE_LABEL_AUTH_FAILURE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1172;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until auth failure unlock.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1173;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the remaining auth failure count.
   */
  public static final int MSGID_PWPSTATE_LABEL_REMAINING_AUTH_FAILURE_COUNT =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1174;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the last login time.
   */
  public static final int MSGID_PWPSTATE_LABEL_LAST_LOGIN_TIME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1175;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until idle lockout.
   */
  public static final int MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_IDLE_LOCKOUT =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1176;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the password reset state.
   */
  public static final int MSGID_PWPSTATE_LABEL_PASSWORD_RESET_STATE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1177;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until password reset lockout.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1178;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the grace login use times.
   */
  public static final int MSGID_PWPSTATE_LABEL_GRACE_LOGIN_USE_TIMES =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1179;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the remaining grace login count.
   */
  public static final int MSGID_PWPSTATE_LABEL_REMAINING_GRACE_LOGIN_COUNT =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1180;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the password changed by required time.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_PASSWORD_CHANGED_BY_REQUIRED_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1181;
  /**
   * The message ID for the message that will be used as the label when
   * displaying the seconds until required change time.
   */
  public static final int
       MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_REQUIRED_CHANGE_TIME =
            CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1182;
  /**
   * The message ID for the message that will be used if the response contained
   * an unknown or invalid operation type.  This takes a single argument, which
   * is the invalid operation type.
   */
  public static final int MSGID_PWPSTATE_INVALID_RESPONSE_OP_TYPE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1183;
  /**
   * The message ID for the message that will be used if two arguments that are
   * mutually exclusive were both provided.  This takes two arguments, which are
   * the long identifiers for the mutually-exclusive command line arguments.
   */
  public static final int MSGID_PWPSTATE_MUTUALLY_EXCLUSIVE_ARGUMENTS =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1184;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to perform SSL initialization.  This takes a single argument, which
   * is a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_CANNOT_INITIALIZE_SSL =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1185;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to parse a SASL option string.  This takes a single argument, which
   * is the SASL option string.
   */
  public static final int MSGID_PWPSTATE_CANNOT_PARSE_SASL_OPTION =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1186;
  /**
   * The message ID for the message that will be used if SASL options were used
   * without specifying the SASL mechanism.  This does not take any arguments.
   */
  public static final int MSGID_PWPSTATE_NO_SASL_MECHANISM =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1187;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to determine the port to use to communicate with the Directory
   * Server.  This takes two arguments, which are the name of the port argument
   * and a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_CANNOT_DETERMINE_PORT =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1188;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to connect to the Directory Server.  This takes a single argument,
   * which is a message explaining the problem that occurred.
   */
  public static final int MSGID_PWPSTATE_CANNOT_CONNECT =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1189;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -10634,6 +11546,278 @@
    registerMessage(MSGID_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_SUMMARY,
                    "Display summary usage information");
    registerMessage(MSGID_PWPSTATE_TOOL_DESCRIPTION,
                    "This utility may be used to retrieve and manipulate " +
                    "the values of password policy state variables");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_HOST,
                    "Directory server hostname or IP address");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_PORT,
                    "Directory server port number");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_USESSL,
                    "Use SSL for secure communication with the server");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_USESTARTTLS,
                    "Use StartTLS to secure communication with the server");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_BINDDN,
                    "The DN to use to bind to the server");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_BINDPW,
                    "The password to use to bind to the server");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_BINDPWFILE,
                    "The path to the file containing the bind password");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_TARGETDN,
                    "The DN of the user entry for which to get and set " +
                    "password policy state information");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_SASLOPTIONS,
                    "SASL bind options");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_TRUST_ALL,
                    "Trust all server SSL certificates");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_KSFILE,
                    "Certificate keystore path");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_KSPW,
                    "Certificate keystore PIN");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_KSPWFILE,
                    "Certificate keystore PIN file");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_TSFILE,
                    "Certificate trust store path");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_TSPW,
                    "Certificate trust store PIN");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_TSPWFILE,
                    "Certificate trust store PIN file");
    registerMessage(MSGID_PWPSTATE_DESCRIPTION_SHOWUSAGE,
                    "Display this usage information");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_ALL,
                    "Display all password policy state information for the " +
                    "user");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_POLICY_DN,
                    "Display the DN of the password policy for the user");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_ACCOUNT_DISABLED_STATE,
                    "Display information about whether the user account has " +
                    "been administratively disabled");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_ACCOUNT_DISABLED_STATE,
                    "Specify whether the user account has been " +
                    "administratively disabled");
    registerMessage(MSGID_DESCRIPTION_OPERATION_BOOLEAN_VALUE,
                    "'true' to indicate that the account is disabled, or " +
                    "'false' to indicate that it is not disabled");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_DISABLED_STATE,
                    "Clear account disabled state information from the user " +
                    "account");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_ACCOUNT_EXPIRATION_TIME,
                    "Display when the user account will expire");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_ACCOUNT_EXPIRATION_TIME,
                    "Specify when the user account will expire");
    registerMessage(MSGID_DESCRIPTION_OPERATION_TIME_VALUE,
                    "A timestamp value using the generalized time syntax");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_EXPIRATION_TIME,
                    "Clear account expiration time information from the user " +
                    "account");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
         "Display the length of time in seconds until the user account " +
         "expires");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_CHANGED_TIME,
                    "Display the time that the user's password was last " +
                    "changed");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_CHANGED_TIME,
                    "Specify the time that the user's password was last " +
                    "changed.  This should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_CHANGED_TIME,
                    "Clear information about the time that the user's " +
                    "password was last changed.  This should be used only " +
                    "for testing purposes");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_EXPIRATION_WARNED_TIME,
         "Display the time that the user first received an expiration " +
         "warning notice");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_EXPIRATION_WARNED_TIME,
         "Specify the time that the user first received an expiration " +
         "warning notice.  This should be used only for testing purposes");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME,
         "Clear information about the time that the user first received an " +
         "expiration warning notice.  This should be used only for testing " +
         "purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP,
                    "Display length of time in seconds until the user's " +
                    "password expires");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP_WARNING,
         "Display the length of time in seconds until the user should start " +
         "receiving password expiration warning notices");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_AUTH_FAILURE_TIMES,
                    "Display the authentication failure times for the user");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_ADD_AUTH_FAILURE_TIME,
                    "Add an authentication failure time to the user " +
                    "account.  This should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_AUTH_FAILURE_TIMES,
                    "Specify the authentication failure times for the user.  " +
                    "This should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_OPERATION_TIME_VALUES,
                    "A timestamp value using the generalized time syntax.  " +
                    "Multiple timestamp values may be given by providing " +
                    "this argument multiple times");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_AUTH_FAILURE_TIMES,
                    "Clear authentication failure time information from the " +
                    "user's account.  This should be used only for testing " +
                    "purposes");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK,
         "Display the length of time in seconds until the authentication " +
         "failure lockout expires");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_REMAINING_AUTH_FAILURE_COUNT,
                    "Display the number of remaining authentication failures " +
                    "until the user's account is locked");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_LAST_LOGIN_TIME,
                    "Display the time that the user last authenticated to " +
                    "the server");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_LAST_LOGIN_TIME,
                    "Specify the time that the user last authenticated to " +
                    "the server.  This should be used only for testing " +
                    "purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_LAST_LOGIN_TIME,
                    "Clear the time that the user last authenticated to the " +
                    "server.  This should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_IDLE_LOCKOUT,
                    "Display the length of time in seconds until user's " +
                    "account is locked because it has remained idle for " +
                    "too long");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_RESET_STATE,
                    "Display information about whether the user will be " +
                    "required to change his or her password on the next " +
                    "successful authentication");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_RESET_STATE,
                    "Specify whether the user will be required to change his " +
                    "or her password on the next successful authentication.  " +
                    "This should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_RESET_STATE,
                    "Clear information about whether the user will be " +
                    "required to change his or her password on the next " +
                    "successful authentication.  This should be used only " +
                    "for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_RESET_LOCKOUT,
                    "Display the length of time in seconds until user's " +
                    "account is locked because the user failed to change the " +
                    "password in a timely manner after an administrative " +
                    "reset");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_GRACE_LOGIN_USE_TIMES,
                    "Display the grace login use times for the user");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_ADD_GRACE_LOGIN_USE_TIME,
                    "Add a grace login use time to the user account.  This " +
                    "should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_GRACE_LOGIN_USE_TIMES,
                    "Specify the grace login use times for the user.  This " +
                    "should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_CLEAR_GRACE_LOGIN_USE_TIMES,
                    "Clear the set of grace login use times for the user.  " +
                    "This should be used only for testing purposes");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_REMAINING_GRACE_LOGIN_COUNT,
                    "Display the number of grace logins remaining for the " +
                    "user");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_GET_PW_CHANGED_BY_REQUIRED_TIME,
                    "Display the required password change time with which " +
                    "the user last complied");
    registerMessage(MSGID_DESCRIPTION_PWPSTATE_SET_PW_CHANGED_BY_REQUIRED_TIME,
                    "Specify the required password change time with which " +
                    "the user last complied.  This should be used only for " +
                    "testing purposes");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_CLEAR_PW_CHANGED_BY_REQUIRED_TIME,
         "Clear information about the required password change time with " +
         "which the user last complied.  This should be used only for " +
         "testing purposes");
    registerMessage(
         MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
         "Display the length of time in seconds that the user has remaining " +
         "to change his or her password before the account becomes locked " +
         "due to the required change time");
    registerMessage(MSGID_PWPSTATE_MUTUALLY_EXCLUSIVE_ARGUMENTS,
                    "ERROR:  You may not provide both the %s and the %s " +
                    "arguments");
    registerMessage(MSGID_PWPSTATE_CANNOT_INITIALIZE_SSL,
                    "ERROR:  Unable to perform SSL initialization:  %s");
    registerMessage(MSGID_PWPSTATE_CANNOT_PARSE_SASL_OPTION,
                    "ERROR:  The provided SASL option string \"%s\" could " +
                    "not be parsed in the form \"name=value\"");
    registerMessage(MSGID_PWPSTATE_NO_SASL_MECHANISM,
                    "ERROR:  One or more SASL options were provided, but " +
                    "none of them were the \"mech\" option to specify which " +
                    "SASL mechanism should be used");
    registerMessage(MSGID_PWPSTATE_CANNOT_DETERMINE_PORT,
                    "ERROR:  Cannot parse the value of the %s argument as " +
                    "an integer value between 1 and 65535:  %s");
    registerMessage(MSGID_PWPSTATE_CANNOT_CONNECT,
                    "ERROR:  Cannot establish a connection to the " +
                    "Directory Server:  %s");
    registerMessage(MSGID_PWPSTATE_NO_SUBCOMMAND,
                    "No subcommand was provided to indicate which password " +
                    "policy state operation should be performed");
    registerMessage(MSGID_PWPSTATE_INVALID_BOOLEAN_VALUE,
                    "The provided value '%s' was invalid for the requested " +
                    "operation.  A Boolean value of either 'true' or 'false' " +
                    "was expected");
    registerMessage(MSGID_PWPSTATE_NO_BOOLEAN_VALUE,
                    "No value was specified, but the requested operation " +
                    "requires a Boolean value of either 'true' or 'false'");
    registerMessage(MSGID_PWPSTATE_INVALID_SUBCOMMAND,
                    "Unrecognized subcommand '%s'");
    registerMessage(MSGID_PWPSTATE_CANNOT_SEND_REQUEST_EXTOP,
                    "An error occurred while attempting to send the request " +
                    "to the server:  %s");
    registerMessage(MSGID_PWPSTATE_CONNECTION_CLOSED_READING_RESPONSE,
                    "The Directory Server closed the connection before the " +
                    "response could be read");
    registerMessage(MSGID_PWPSTATE_REQUEST_FAILED,
                    "The server was unable to process the request:  result " +
                    "code %d (%s), error message '%s'");
    registerMessage(MSGID_PWPSTATE_CANNOT_DECODE_RESPONSE_MESSAGE,
                    "The server was unable to decode the response message " +
                    "from the server:  %s");
    registerMessage(MSGID_PWPSTATE_CANNOT_DECODE_RESPONSE_OP,
                    "Unable to decode information about an operation " +
                    "contained in the response:  %s");
    registerMessage(MSGID_PWPSTATE_LABEL_PASSWORD_POLICY_DN,
                    "Password Policy DN");
    registerMessage(MSGID_PWPSTATE_LABEL_ACCOUNT_DISABLED_STATE,
                    "Account Is Disabled");
    registerMessage(MSGID_PWPSTATE_LABEL_ACCOUNT_EXPIRATION_TIME,
                    "Account Expiration Time");
    registerMessage(MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
                    "Seconds Until Account Expiration");
    registerMessage(MSGID_PWPSTATE_LABEL_PASSWORD_CHANGED_TIME,
                    "Password Changed Time");
    registerMessage(MSGID_PWPSTATE_LABEL_PASSWORD_EXPIRATION_WARNED_TIME,
                    "Password Expiration Warned Time");
    registerMessage(MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION,
                    "Seconds Until Password Expiration");
    registerMessage(
         MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
         "Seconds Until Password Expiration Warning");
    registerMessage(MSGID_PWPSTATE_LABEL_AUTH_FAILURE_TIMES,
                    "Authentication Failure Times");
    registerMessage(MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK,
                    "Seconds Until Authentication Failure Unlock");
    registerMessage(MSGID_PWPSTATE_LABEL_REMAINING_AUTH_FAILURE_COUNT,
                    "Remaining Authentication Failure Count");
    registerMessage(MSGID_PWPSTATE_LABEL_LAST_LOGIN_TIME,
                    "Last Login Time");
    registerMessage(MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_IDLE_LOCKOUT,
                    "Seconds Until Idle Account Lockout");
    registerMessage(MSGID_PWPSTATE_LABEL_PASSWORD_RESET_STATE,
                    "Password Is Reset");
    registerMessage(MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
                    "Seconds Until Password Reset Lockout");
    registerMessage(MSGID_PWPSTATE_LABEL_GRACE_LOGIN_USE_TIMES,
                    "Grace Login Use Times");
    registerMessage(MSGID_PWPSTATE_LABEL_REMAINING_GRACE_LOGIN_COUNT,
                    "Remaining Grace Login Count");
    registerMessage(MSGID_PWPSTATE_LABEL_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                    "Password Changed by Required Time");
    registerMessage(MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
                    "Seconds Until Required Change Time");
    registerMessage(MSGID_PWPSTATE_INVALID_RESPONSE_OP_TYPE,
                    "Unrecognized or invalid operation type:  %s");
  }
}
opends/src/server/org/opends/server/tools/ManageAccount.java
New file
@@ -0,0 +1,1688 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicInteger;
import org.opends.server.protocols.asn1.ASN1Element;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.asn1.ASN1Reader;
import org.opends.server.protocols.asn1.ASN1Sequence;
import org.opends.server.protocols.asn1.ASN1Writer;
import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp;
import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
import org.opends.server.protocols.ldap.LDAPMessage;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.types.NullOutputStream;
import org.opends.server.util.args.Argument;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.FileBasedArgument;
import org.opends.server.util.args.IntegerArgument;
import org.opends.server.util.args.MultiChoiceArgument;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import static org.opends.server.extensions.
                   PasswordPolicyStateExtendedOperation.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ToolMessages.*;
import static org.opends.server.tools.ToolConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class provides a tool that can be used to perform various kinds of
 * account management using the password policy state extended operation.
 */
public class ManageAccount
{
  /**
   * The fully-qualified name of this class.
   */
  private static final String CLASS_NAME =
       "org.opends.server.tools.ManageAccount";
  /**
   * The name of the subcommand that will be used to get all password policy
   * state information for the user.
   */
  private static final String SC_GET_ALL = "get-all";
  /**
   * The name of the subcommand that will be used to get the DN of the password
   * policy for a given user.
   */
  private static final String SC_GET_PASSWORD_POLICY_DN =
       "get-password-policy-dn";
  /**
   * The name of the subcommand that will be used to get the disabled state for
   * a user.
   */
  private static final String SC_GET_ACCOUNT_DISABLED_STATE =
       "get-account-is-disabled";
  /**
   * The name of the subcommand that will be used to set the disabled state for
   * a user.
   */
  private static final String SC_SET_ACCOUNT_DISABLED_STATE =
       "set-account-is-disabled";
  /**
   * The name of the subcommand that will be used to clear the disabled state
   * for a user.
   */
  private static final String SC_CLEAR_ACCOUNT_DISABLED_STATE =
       "clear-account-is-disabled";
  /**
   * The name of the subcommand that will be used to get the account expiration
   * time.
   */
  private static final String SC_GET_ACCOUNT_EXPIRATION_TIME =
       "get-account-expiration-time";
  /**
   * The name of the subcommand that will be used to set the account expiration
   * time.
   */
  private static final String SC_SET_ACCOUNT_EXPIRATION_TIME =
       "set-account-expiration-time";
  /**
   * The name of the subcommand that will be used to clear the account
   * expiration time.
   */
  private static final String SC_CLEAR_ACCOUNT_EXPIRATION_TIME =
       "clear-account-expiration-time";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the account expires.
   */
  private static final String SC_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION =
       "get-seconds-until-account-expiration";
  /**
   * The name of the subcommand that will be used to get the time the password
   * was last changed.
   */
  private static final String SC_GET_PASSWORD_CHANGED_TIME =
       "get-password-changed-time";
  /**
   * The name of the subcommand that will be used to set the time the password
   * was last changed.
   */
  private static final String SC_SET_PASSWORD_CHANGED_TIME =
       "set-password-changed-time";
  /**
   * The name of the subcommand that will be used to clear the time the password
   * was last changed.
   */
  private static final String SC_CLEAR_PASSWORD_CHANGED_TIME =
       "clear-password-changed-time";
  /**
   * The name of the subcommand that will be used to get the time the user was
   * first warned about an upcoming password expiration.
   */
  private static final String SC_GET_PASSWORD_EXP_WARNED_TIME =
       "get-password-expiration-warned-time";
  /**
   * The name of the subcommand that will be used to set the time the user was
   * first warned about an upcoming password expiration.
   */
  private static final String SC_SET_PASSWORD_EXP_WARNED_TIME =
       "set-password-expiration-warned-time";
  /**
   * The name of the subcommand that will be used to clear the time the user was
   * first warned about an upcoming password expiration.
   */
  private static final String SC_CLEAR_PASSWORD_EXP_WARNED_TIME =
       "clear-password-expiration-warned-time";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the password expires.
   */
  private static final String SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION =
       "get-seconds-until-password-expiration";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the user is first warned about an upcoming password expiration.
   */
  private static final String SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING =
       "get-seconds-until-password-expiration-warning";
  /**
   * The name of the subcommand that will be used to get the authentication
   * failure times for the user.
   */
  private static final String SC_GET_AUTHENTICATION_FAILURE_TIMES =
       "get-authentication-failure-times";
  /**
   * The name of the subcommand that will be used to add an authentication
   * failure time for the user.
   */
  private static final String SC_ADD_AUTHENTICATION_FAILURE_TIME =
       "add-authentication-failure-time";
  /**
   * The name of the subcommand that will be used to set the authentication
   * failure times for the user.
   */
  private static final String SC_SET_AUTHENTICATION_FAILURE_TIMES =
       "set-authentication-failure-times";
  /**
   * The name of the subcommand that will be used to clear the authentication
   * failure times for the user.
   */
  private static final String SC_CLEAR_AUTHENTICATION_FAILURE_TIMES =
       "clear-authentication-failure-times";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the user's account is unlocked.
   */
  private static final String
       SC_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK =
            "get-seconds-until-authentication-failure-unlock";
  /**
   * The name of the subcommand that will be used to get the number of remaining
   * authentication failures for the user.
   */
  private static final String SC_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT =
       "get-remaining-authentication-failure-count";
  /**
   * The name of the subcommand that will be used to get the last login time for
   * the user.
   */
  private static final String SC_GET_LAST_LOGIN_TIME =
       "get-last-login-time";
  /**
   * The name of the subcommand that will be used to set the last login time for
   * the user.
   */
  private static final String SC_SET_LAST_LOGIN_TIME =
       "set-last-login-time";
  /**
   * The name of the subcommand that will be used to clear the last login time
   * for the user.
   */
  private static final String SC_CLEAR_LAST_LOGIN_TIME =
       "clear-last-login-time";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the account is idle locked.
   */
  private static final String SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT =
       "get-seconds-until-idle-lockout";
  /**
   * The name of the subcommand that will be used to get the password reset
   * state for a user.
   */
  private static final String SC_GET_PASSWORD_RESET_STATE =
       "get-password-is-reset";
  /**
   * The name of the subcommand that will be used to set the password reset
   * state for a user.
   */
  private static final String SC_SET_PASSWORD_RESET_STATE =
       "set-password-is-reset";
  /**
   * The name of the subcommand that will be used to clear the password reset
   * state for a user.
   */
  private static final String SC_CLEAR_PASSWORD_RESET_STATE =
       "clear-password-is-reset";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the password reset lockout occurs.
   */
  private static final String SC_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT =
       "get-seconds-until-password-reset-lockout";
  /**
   * The name of the subcommand that will be used to get the grace login use
   * times for the user.
   */
  private static final String SC_GET_GRACE_LOGIN_USE_TIMES =
       "get-grace-login-use-times";
  /**
   * The name of the subcommand that will be used to add a grace login use time
   * for the user.
   */
  private static final String SC_ADD_GRACE_LOGIN_USE_TIME =
       "add-grace-login-use-time";
  /**
   * The name of the subcommand that will be used to set the grace login use
   * times for the user.
   */
  private static final String SC_SET_GRACE_LOGIN_USE_TIMES =
       "set-grace-login-use-times";
  /**
   * The name of the subcommand that will be used to clear the grace login use
   * times for the user.
   */
  private static final String SC_CLEAR_GRACE_LOGIN_USE_TIMES =
       "clear-grace-login-use-times";
  /**
   * The name of the subcommand that will be used to get number of remaining
   * grace logins for the user.
   */
  private static final String SC_GET_REMAINING_GRACE_LOGIN_COUNT =
       "get-remaining-grace-login-count";
  /**
   * The name of the subcommand that will be used to get the password changed by
   * required time for the user.
   */
  private static final String SC_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME =
       "get-password-changed-by-required-time";
  /**
   * The name of the subcommand that will be used to set the password changed by
   * required time for the user.
   */
  private static final String SC_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME =
       "set-password-changed-by-required-time";
  /**
   * The name of the subcommand that will be used to clear the password changed
   * by required time for the user.
   */
  private static final String SC_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME =
       "clear-password-changed-by-required-time";
  /**
   * The name of the subcommand that will be used to get the length of time
   * before the user is required to change his/her password due to the required
   * change time.
   */
  private static final String SC_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME =
       "get-seconds-until-required-change-time";
  /**
   * The name of the argument that will be used for holding the value(s) to use
   * for the target operation.
   */
  private static final String ARG_OP_VALUE = "opvalue";
  /**
   * The value that will be used when encoding a password policy state operation
   * that should not have any values.
   */
  private static final String NO_VALUE = null;
  // The ASN.1 reader used to read responses from the server.
  private static ASN1Reader asn1Reader;
  // The ASN.1 writer used to send requests to the server.
  private static ASN1Writer asn1Writer;
  // The counter that will be used for LDAP message IDs.
  private static AtomicInteger nextMessageID;
  // The connection to the server.
  private static LDAPConnection connection;
  // The print stream to use when writing messages to standard error.
  private static PrintStream err;
  // The print stream to use when writing messages to standard output.
  private static PrintStream out;
  // The DN of the user to target with the operation.
  private static String targetDNString;
  // The argument parser for this tool.
  private static SubCommandArgumentParser argParser;
  /**
   * Parses the command-line arguments, connects to the server, and performs the
   * appropriate processing.
   *
   * @param  args  The command-line arguments provided to this program.
   */
  public static void main(String[] args)
  {
    int returnCode = main(args, System.out, System.err);
    if (returnCode != 0)
    {
      System.exit(returnCode);
    }
  }
  /**
   * Parses the command-line arguments, connects to the server, and performs the
   * appropriate processing.
   *
   * @param  args       The command-line arguments provided to this program.
   * @param  outStream  The output stream to use for standard output, or
   *                    {@code null} if standard output is not needed.
   * @param  errStream  The output stream to use for standard error, or
   *                    {@code null} if standard error is not needed.
   *
   * @return  A result code indicating whether the processing was successful.
   */
  public static int main(String[] args, OutputStream outStream,
                         OutputStream errStream)
  {
    if (outStream == null)
    {
      out = NullOutputStream.printStream();
    }
    else
    {
      out = new PrintStream(outStream);
    }
    if (errStream == null)
    {
      err = NullOutputStream.printStream();
    }
    else
    {
      err = new PrintStream(errStream);
    }
    // Parse the command-line arguments provided to the program.
    int result = parseArgsAndConnect(args);
    if (result < 0)
    {
      // This should only happen if we're only displaying usage information or
      // doing something else other than actually running the tool.
      return LDAPResultCode.SUCCESS;
    }
    else if (result != LDAPResultCode.SUCCESS)
    {
      return result;
    }
    try
    {
      // Use the subcommand provided to figure out how to encode the request.
      ArrayList<ASN1Element> opElements = new ArrayList<ASN1Element>(1);
      result = processSubcommand(opElements);
      if (result != LDAPResultCode.SUCCESS)
      {
        return result;
      }
      // Generate the extended request and send it to the server.
      ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(2);
      valueElements.add(new ASN1OctetString(targetDNString));
      if (! opElements.isEmpty())
      {
        valueElements.add(new ASN1Sequence(opElements));
      }
      ASN1OctetString requestValue =
           new ASN1OctetString(new ASN1Sequence(valueElements).encode());
      ExtendedRequestProtocolOp extendedRequest =
           new ExtendedRequestProtocolOp(OID_PASSWORD_POLICY_STATE_EXTOP,
                                         requestValue);
      LDAPMessage requestMessage =
           new LDAPMessage(nextMessageID.getAndIncrement(), extendedRequest);
      try
      {
        asn1Writer.writeElement(requestMessage.encode());
      }
      catch (Exception e)
      {
        int    msgID   = MSGID_PWPSTATE_CANNOT_SEND_REQUEST_EXTOP;
        String message = getMessage(msgID, getExceptionMessage(e));
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
      }
      // Read the response from the server.
      ArrayList<ASN1Element> responseOpElements;
      try
      {
        ASN1Element responseElement = asn1Reader.readElement();
        if (responseElement == null)
        {
          int    msgID   = MSGID_PWPSTATE_CONNECTION_CLOSED_READING_RESPONSE;
          String message = getMessage(msgID);
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
        }
        LDAPMessage responseMessage =
             LDAPMessage.decode(responseElement.decodeAsSequence());
        ExtendedResponseProtocolOp extendedResponse =
             responseMessage.getExtendedResponseProtocolOp();
        int resultCode = extendedResponse.getResultCode();
        if (resultCode != LDAPResultCode.SUCCESS)
        {
          int msgID = MSGID_PWPSTATE_REQUEST_FAILED;
          String message =
               getMessage(msgID, resultCode,
                          LDAPResultCode.toString(resultCode),
                          String.valueOf(extendedResponse.getErrorMessage()));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return resultCode;
        }
        ASN1Sequence valueSequence =
             ASN1Sequence.decodeAsSequence(extendedResponse.getValue().value());
        responseOpElements =
             valueSequence.elements().get(1).decodeAsSequence().elements();
      }
      catch (Exception e)
      {
        int    msgID   = MSGID_PWPSTATE_CANNOT_DECODE_RESPONSE_MESSAGE;
        String message = getMessage(msgID, getExceptionMessage(e));
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
      }
      // Get the response value and parse its individual elements.
      for (ASN1Element opElement : responseOpElements)
      {
        int opType;
        ArrayList<String> opValues;
        try
        {
          ASN1Sequence opSequence = opElement.decodeAsSequence();
          ArrayList<ASN1Element> elements = opSequence.elements();
          opType = elements.get(0).decodeAsEnumerated().intValue();
          opValues = new ArrayList<String>();
          if (elements.size() == 2)
          {
            for (ASN1Element e : elements.get(1).decodeAsSequence().elements())
            {
              opValues.add(e.decodeAsOctetString().stringValue());
            }
          }
        }
        catch (Exception e)
        {
          int msgID = MSGID_PWPSTATE_CANNOT_DECODE_RESPONSE_OP;
          String message = getMessage(msgID, getExceptionMessage(e));
          err.println(wrapText(message, MAX_LINE_WIDTH));
          continue;
        }
        switch (opType)
        {
          case OP_GET_PASSWORD_POLICY_DN:
            int msgID = MSGID_PWPSTATE_LABEL_PASSWORD_POLICY_DN;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_ACCOUNT_DISABLED_STATE:
            msgID = MSGID_PWPSTATE_LABEL_ACCOUNT_DISABLED_STATE;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_ACCOUNT_EXPIRATION_TIME:
            msgID = MSGID_PWPSTATE_LABEL_ACCOUNT_EXPIRATION_TIME;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION:
            msgID = MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_ACCOUNT_EXPIRATION;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_PASSWORD_CHANGED_TIME:
            msgID = MSGID_PWPSTATE_LABEL_PASSWORD_CHANGED_TIME;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_PASSWORD_EXPIRATION_WARNED_TIME:
            msgID = MSGID_PWPSTATE_LABEL_PASSWORD_EXPIRATION_WARNED_TIME;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION:
            msgID = MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING:
            msgID =
                 MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_AUTHENTICATION_FAILURE_TIMES:
            msgID = MSGID_PWPSTATE_LABEL_AUTH_FAILURE_TIMES;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK:
            msgID = MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT:
            msgID = MSGID_PWPSTATE_LABEL_REMAINING_AUTH_FAILURE_COUNT;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_LAST_LOGIN_TIME:
            msgID = MSGID_PWPSTATE_LABEL_LAST_LOGIN_TIME;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT:
            msgID = MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_IDLE_LOCKOUT;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_PASSWORD_RESET_STATE:
            msgID = MSGID_PWPSTATE_LABEL_PASSWORD_RESET_STATE;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT:
            msgID = MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_GRACE_LOGIN_USE_TIMES:
            msgID = MSGID_PWPSTATE_LABEL_GRACE_LOGIN_USE_TIMES;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_REMAINING_GRACE_LOGIN_COUNT:
            msgID = MSGID_PWPSTATE_LABEL_REMAINING_GRACE_LOGIN_COUNT;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME:
            msgID = MSGID_PWPSTATE_LABEL_PASSWORD_CHANGED_BY_REQUIRED_TIME;
            printLabelAndValues(msgID, opValues);
            break;
          case OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME:
            msgID = MSGID_PWPSTATE_LABEL_SECONDS_UNTIL_REQUIRED_CHANGE_TIME;
            printLabelAndValues(msgID, opValues);
            break;
          default:
            msgID = MSGID_PWPSTATE_INVALID_RESPONSE_OP_TYPE;
            String message = getMessage(msgID, opType);
            err.println(wrapText(message, MAX_LINE_WIDTH));
            break;
        }
      }
      // If we've gotten here, then everything completed successfully.
      return 0;
    }
    finally
    {
      // Close the connection to the server if it's active.
      if (connection != null)
      {
        connection.close(nextMessageID);
      }
    }
  }
  /**
   * Initializes the argument parser for this tool, parses the provided
   * arguments, and establishes a connection to the server.
   *
   * @return  A result code that indicates the result of the processing.  A
   *          value of zero indicates that all processing completed
   *          successfully.  A value of -1 indicates that only the usage
   *          information was displayed and no further action is required.
   */
  private static int parseArgsAndConnect(String[] args)
  {
    int msgID = MSGID_PWPSTATE_TOOL_DESCRIPTION;
    argParser = new SubCommandArgumentParser(CLASS_NAME, getMessage(msgID),
                                             false);
    BooleanArgument   showUsage;
    BooleanArgument   trustAll;
    BooleanArgument   useSSL;
    BooleanArgument   useStartTLS;
    FileBasedArgument bindPWFile;
    FileBasedArgument keyStorePWFile;
    FileBasedArgument trustStorePWFile;
    IntegerArgument   port;
    StringArgument    bindDN;
    StringArgument    bindPW;
    StringArgument    certNickname;
    StringArgument    host;
    StringArgument    keyStoreFile;
    StringArgument    keyStorePW;
    StringArgument    saslOption;
    StringArgument    targetDN;
    StringArgument    trustStoreFile;
    StringArgument    trustStorePW;
    try
    {
      host = new StringArgument("host", OPTION_SHORT_HOST,
                                OPTION_LONG_HOST, false, false, true,
                                OPTION_VALUE_HOST, "127.0.0.1", null,
                                MSGID_PWPSTATE_DESCRIPTION_HOST);
      argParser.addGlobalArgument(host);
      port = new IntegerArgument("port", OPTION_SHORT_PORT,
                                 OPTION_LONG_PORT, false, false, true,
                                 OPTION_VALUE_PORT, 389, null, true, 1,
                                 true, 65535, MSGID_PWPSTATE_DESCRIPTION_PORT);
      argParser.addGlobalArgument(port);
      useSSL = new BooleanArgument("usessl", OPTION_SHORT_USE_SSL,
                                   OPTION_LONG_USE_SSL,
                                   MSGID_PWPSTATE_DESCRIPTION_USESSL);
      argParser.addGlobalArgument(useSSL);
      useStartTLS = new BooleanArgument("usestarttls", OPTION_SHORT_START_TLS,
                                        OPTION_LONG_START_TLS,
                                        MSGID_PWPSTATE_DESCRIPTION_USESTARTTLS);
      argParser.addGlobalArgument(useStartTLS);
      bindDN = new StringArgument("binddn", OPTION_SHORT_BINDDN,
                                  OPTION_LONG_BINDDN, false, false, true,
                                  OPTION_VALUE_BINDDN, null, null,
                                  MSGID_PWPSTATE_DESCRIPTION_BINDDN);
      argParser.addGlobalArgument(bindDN);
      bindPW = new StringArgument("bindpw", OPTION_SHORT_BINDPWD,
                                  OPTION_LONG_BINDPWD, false, false,
                                  true,
                                  OPTION_VALUE_BINDPWD, null, null,
                                  MSGID_PWPSTATE_DESCRIPTION_BINDPW);
      argParser.addGlobalArgument(bindPW);
      bindPWFile = new FileBasedArgument("bindpwfile",
                                         OPTION_SHORT_BINDPWD_FILE,
                                         OPTION_LONG_BINDPWD_FILE,
                                         false, false,
                                         OPTION_VALUE_BINDPWD_FILE,
                                         null, null,
                                         MSGID_PWPSTATE_DESCRIPTION_BINDPWFILE);
      argParser.addGlobalArgument(bindPWFile);
      targetDN = new StringArgument("targetdn", 'b', "targetDN", true, false,
                                    true, "{targetDN}", null, null,
                                    MSGID_PWPSTATE_DESCRIPTION_TARGETDN);
      argParser.addGlobalArgument(targetDN);
      saslOption = new StringArgument("sasloption", OPTION_SHORT_SASLOPTION,
                                      OPTION_LONG_SASLOPTION, false,
                                      true, true,
                                      OPTION_VALUE_SASLOPTION, null, null,
                                      MSGID_PWPSTATE_DESCRIPTION_SASLOPTIONS);
      argParser.addGlobalArgument(saslOption);
      trustAll = new BooleanArgument("trustall", 'X', "trustAll",
                                     MSGID_PWPSTATE_DESCRIPTION_TRUST_ALL);
      argParser.addGlobalArgument(trustAll);
      keyStoreFile = new StringArgument("keystorefile",
                                        OPTION_SHORT_KEYSTOREPATH,
                                        OPTION_LONG_KEYSTOREPATH,
                                        false, false, true,
                                        OPTION_VALUE_KEYSTOREPATH,
                                        null, null,
                                        MSGID_PWPSTATE_DESCRIPTION_KSFILE);
      argParser.addGlobalArgument(keyStoreFile);
      keyStorePW = new StringArgument("keystorepw", OPTION_SHORT_KEYSTORE_PWD,
                                      OPTION_LONG_KEYSTORE_PWD,
                                      false, false, true,
                                      OPTION_VALUE_KEYSTORE_PWD,
                                      null, null,
                                      MSGID_PWPSTATE_DESCRIPTION_KSPW);
      argParser.addGlobalArgument(keyStorePW);
      keyStorePWFile = new FileBasedArgument("keystorepwfile",
                                OPTION_SHORT_KEYSTORE_PWD_FILE,
                                OPTION_LONG_KEYSTORE_PWD_FILE, false, false,
                                OPTION_VALUE_KEYSTORE_PWD_FILE, null, null,
                                MSGID_PWPSTATE_DESCRIPTION_KSPWFILE);
      argParser.addGlobalArgument(keyStorePWFile);
      certNickname = new StringArgument("certnickname", 'N', "certNickname",
                                        false, false, true, "{nickname}", null,
                                        null, MSGID_DESCRIPTION_CERT_NICKNAME);
      argParser.addGlobalArgument(certNickname);
      trustStoreFile = new StringArgument("truststorefile",
                                          OPTION_SHORT_TRUSTSTOREPATH,
                                          OPTION_LONG_TRUSTSTOREPATH,
                                          false, false, true,
                                          OPTION_VALUE_TRUSTSTOREPATH,
                                          null, null,
                                          MSGID_PWPSTATE_DESCRIPTION_TSFILE);
      argParser.addGlobalArgument(trustStoreFile);
      trustStorePW = new StringArgument("truststorepw", 'T',
                                        OPTION_LONG_TRUSTSTORE_PWD,
                                        false, false,
                                        true, OPTION_VALUE_TRUSTSTORE_PWD, null,
                                        null, MSGID_PWPSTATE_DESCRIPTION_TSPW);
      argParser.addGlobalArgument(trustStorePW);
      trustStorePWFile = new FileBasedArgument("truststorepwfile",
                                  OPTION_SHORT_TRUSTSTORE_PWD_FILE,
                                  OPTION_LONG_TRUSTSTORE_PWD_FILE,
                                  false, false,
                                  OPTION_VALUE_TRUSTSTORE_PWD_FILE, null, null,
                                  MSGID_PWPSTATE_DESCRIPTION_TSPWFILE);
      argParser.addGlobalArgument(trustStorePWFile);
      showUsage = new BooleanArgument("showusage", OPTION_SHORT_HELP,
                                      OPTION_LONG_HELP,
                                      MSGID_PWPSTATE_DESCRIPTION_SHOWUSAGE);
      argParser.addGlobalArgument(showUsage);
      argParser.setUsageArgument(showUsage, out);
      HashSet<String> booleanValues = new HashSet<String>(2);
      booleanValues.add("true");
      booleanValues.add("false");
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_ALL;
      new SubCommand(argParser, SC_GET_ALL, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_POLICY_DN;
      new SubCommand(argParser, SC_GET_PASSWORD_POLICY_DN, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_ACCOUNT_DISABLED_STATE;
      new SubCommand(argParser, SC_GET_ACCOUNT_DISABLED_STATE, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_ACCOUNT_DISABLED_STATE;
      SubCommand sc = new SubCommand(argParser, SC_SET_ACCOUNT_DISABLED_STATE,
                                     msgID);
      sc.addArgument(new MultiChoiceArgument(ARG_OP_VALUE, 'O',
                              "operationValue", true, false, true,
                              "{true|false}", null, null, booleanValues, false,
                              MSGID_DESCRIPTION_OPERATION_BOOLEAN_VALUE));
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_DISABLED_STATE;
      new SubCommand(argParser, SC_CLEAR_ACCOUNT_DISABLED_STATE, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_ACCOUNT_EXPIRATION_TIME;
      new SubCommand(argParser, SC_GET_ACCOUNT_EXPIRATION_TIME, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_ACCOUNT_EXPIRATION_TIME;
      sc = new SubCommand(argParser, SC_SET_ACCOUNT_EXPIRATION_TIME, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, false, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_EXPIRATION_TIME;
      sc = new SubCommand(argParser, SC_CLEAR_ACCOUNT_EXPIRATION_TIME, msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION;
      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_CHANGED_TIME;
      new SubCommand(argParser, SC_GET_PASSWORD_CHANGED_TIME, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_CHANGED_TIME;
      sc = new SubCommand(argParser, SC_SET_PASSWORD_CHANGED_TIME, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, false, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_CHANGED_TIME;
      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_CHANGED_TIME, msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_EXPIRATION_WARNED_TIME;
      new SubCommand(argParser, SC_GET_PASSWORD_EXP_WARNED_TIME, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_EXPIRATION_WARNED_TIME;
      sc = new SubCommand(argParser, SC_SET_PASSWORD_EXP_WARNED_TIME, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, false, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME;
      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_EXP_WARNED_TIME, msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP;
      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
                     msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP_WARNING;
      new SubCommand(argParser,
                     SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_AUTH_FAILURE_TIMES;
      new SubCommand(argParser, SC_GET_AUTHENTICATION_FAILURE_TIMES, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_ADD_AUTH_FAILURE_TIME;
      sc = new SubCommand(argParser, SC_ADD_AUTHENTICATION_FAILURE_TIME, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, true, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_AUTH_FAILURE_TIMES;
      sc = new SubCommand(argParser, SC_SET_AUTHENTICATION_FAILURE_TIMES,
                          msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, true, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUES));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_AUTH_FAILURE_TIMES;
      sc = new SubCommand(argParser, SC_CLEAR_AUTHENTICATION_FAILURE_TIMES,
                          msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK;
      new SubCommand(argParser,
                     SC_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_REMAINING_AUTH_FAILURE_COUNT;
      new SubCommand(argParser, SC_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
                     msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_LAST_LOGIN_TIME;
      new SubCommand(argParser, SC_GET_LAST_LOGIN_TIME, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_LAST_LOGIN_TIME;
      sc = new SubCommand(argParser, SC_SET_LAST_LOGIN_TIME, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, false, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_LAST_LOGIN_TIME;
      sc = new SubCommand(argParser, SC_CLEAR_LAST_LOGIN_TIME, msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_IDLE_LOCKOUT;
      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_PASSWORD_RESET_STATE;
      new SubCommand(argParser, SC_GET_PASSWORD_RESET_STATE, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_PASSWORD_RESET_STATE;
      sc = new SubCommand(argParser, SC_SET_PASSWORD_RESET_STATE, msgID);
      sc.addArgument(new MultiChoiceArgument(ARG_OP_VALUE, 'O',
                              "operationValue", true, false, true,
                              "{true|false}", null, null, booleanValues, false,
                              MSGID_DESCRIPTION_OPERATION_BOOLEAN_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_RESET_STATE;
      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_RESET_STATE, msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_RESET_LOCKOUT;
      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
                     msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_GRACE_LOGIN_USE_TIMES;
      new SubCommand(argParser, SC_GET_GRACE_LOGIN_USE_TIMES, msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_ADD_GRACE_LOGIN_USE_TIME;
      sc = new SubCommand(argParser, SC_ADD_GRACE_LOGIN_USE_TIME, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, true, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_GRACE_LOGIN_USE_TIMES;
      sc = new SubCommand(argParser, SC_SET_GRACE_LOGIN_USE_TIMES, msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, true, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUES));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_GRACE_LOGIN_USE_TIMES;
      sc = new SubCommand(argParser, SC_CLEAR_GRACE_LOGIN_USE_TIMES, msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_REMAINING_GRACE_LOGIN_COUNT;
      new SubCommand(argParser, SC_GET_REMAINING_GRACE_LOGIN_COUNT,
                     msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_PW_CHANGED_BY_REQUIRED_TIME;
      new SubCommand(argParser, SC_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                     msgID);
      msgID = MSGID_DESCRIPTION_PWPSTATE_SET_PW_CHANGED_BY_REQUIRED_TIME;
      sc = new SubCommand(argParser, SC_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                          msgID);
      sc.addArgument(new StringArgument(ARG_OP_VALUE, 'O', "operationValue",
                              false, false, true, "{time}", null, null,
                              MSGID_DESCRIPTION_OPERATION_TIME_VALUE));
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_CLEAR_PW_CHANGED_BY_REQUIRED_TIME;
      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                          msgID);
      sc.setHidden(true);
      msgID = MSGID_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME;
      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
                     msgID);
    }
    catch (ArgumentException ae)
    {
      msgID = MSGID_CANNOT_INITIALIZE_ARGS;
      String message = getMessage(msgID, ae.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
    }
    try
    {
      argParser.parseArguments(args);
    }
    catch (ArgumentException ae)
    {
      msgID = MSGID_ERROR_PARSING_ARGS;
      String message = getMessage(msgID, ae.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      err.println(argParser.getUsage());
      return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
    }
    // If we should just display usage or version information,
    // then exit because it will have already been done.
    if (argParser.usageOrVersionDisplayed())
    {
      return -1;
    }
    // Get the target DN as a string for later use.
    targetDNString = targetDN.getValue();
    // 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 (useSSL.isPresent())
    {
      if (useStartTLS.isPresent())
      {
        msgID = MSGID_PWPSTATE_MUTUALLY_EXCLUSIVE_ARGUMENTS;
        String message = getMessage(msgID, useSSL.getLongIdentifier(),
                                    useStartTLS.getLongIdentifier());
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
      }
      else
      {
        connectionOptions.setUseSSL(true);
      }
    }
    else if (useStartTLS.isPresent())
    {
      connectionOptions.setStartTLS(true);
    }
    // If we should blindly trust any certificate, then install the appropriate
    // SSL connection factory.
    if (useSSL.isPresent() || useStartTLS.isPresent())
    {
      try
      {
        String clientAlias;
        if (certNickname.isPresent())
        {
          clientAlias = certNickname.getValue();
        }
        else
        {
          clientAlias = null;
        }
        SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
        sslConnectionFactory.init(trustAll.isPresent(), keyStoreFile.getValue(),
                                  keyStorePW.getValue(), clientAlias,
                                  trustStoreFile.getValue(),
                                  trustStorePW.getValue());
        connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
      }
      catch (SSLConnectionException sce)
      {
        msgID = MSGID_PWPSTATE_CANNOT_INITIALIZE_SSL;
        String message = getMessage(msgID, sce.getMessage());
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
      }
    }
    // If one or more SASL options were provided, then make sure that one of
    // them was "mech" and specified a valid SASL mechanism.
    if (saslOption.isPresent())
    {
      String             mechanism = null;
      LinkedList<String> options   = new LinkedList<String>();
      for (String s : saslOption.getValues())
      {
        int equalPos = s.indexOf('=');
        if (equalPos <= 0)
        {
          msgID = MSGID_PWPSTATE_CANNOT_PARSE_SASL_OPTION;
          String message = getMessage(msgID, s);
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
        }
        else
        {
          String name  = s.substring(0, equalPos);
          if (name.equalsIgnoreCase("mech"))
          {
            mechanism = s;
          }
          else
          {
            options.add(s);
          }
        }
      }
      if (mechanism == null)
      {
        msgID = MSGID_PWPSTATE_NO_SASL_MECHANISM;
        String message = getMessage(msgID);
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
      }
      connectionOptions.setSASLMechanism(mechanism);
      for (String option : options)
      {
        connectionOptions.addSASLProperty(option);
      }
    }
    // Attempt to connect and authenticate to the Directory Server.
    nextMessageID = new AtomicInteger(1);
    try
    {
      connection = new LDAPConnection(host.getValue(), port.getIntValue(),
                                      connectionOptions, out, err);
      connection.connectToHost(bindDN.getValue(), bindPW.getValue(),
                               nextMessageID);
    }
    catch (ArgumentException ae)
    {
      msgID = MSGID_PWPSTATE_CANNOT_DETERMINE_PORT;
      String message = getMessage(msgID, port.getLongIdentifier(),
                                  ae.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
    }
    catch (LDAPConnectionException lce)
    {
      msgID = MSGID_PWPSTATE_CANNOT_CONNECT;
      String message = getMessage(msgID, lce.getMessage());
      err.println(wrapText(message, MAX_LINE_WIDTH));
      return LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR;
    }
    asn1Reader = connection.getASN1Reader();
    asn1Writer = connection.getASN1Writer();
    return LDAPResultCode.SUCCESS;
  }
  /**
   * Processes the subcommand from the provided argument parser and appends the
   * appropriate operation elements to the given list.
   *
   * @param  opElements  A list into which the operation elements shouold be
   *                     placed.
   *
   * @return  A result code indicating the results of the processing.
   */
  private static int processSubcommand(ArrayList<ASN1Element> opElements)
  {
    int msgID;
    SubCommand subCommand = argParser.getSubCommand();
    if (subCommand == null)
    {
      msgID = MSGID_PWPSTATE_NO_SUBCOMMAND;
      String message = getMessage(msgID);
      err.println(wrapText(message, MAX_LINE_WIDTH));
      err.println(argParser.getUsage());
      return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
    }
    String subCommandName = subCommand.getName();
    if (subCommandName.equals(SC_GET_ALL))
    {
      // The list should stay empty for this one.
    }
    else if (subCommandName.equals(SC_GET_PASSWORD_POLICY_DN))
    {
      opElements.add(encode(OP_GET_PASSWORD_POLICY_DN, NO_VALUE));
    }
    else if (subCommandName.equals(SC_GET_ACCOUNT_DISABLED_STATE))
    {
      opElements.add(encode(OP_GET_ACCOUNT_DISABLED_STATE, NO_VALUE));
    }
    else if (subCommandName.equals(SC_SET_ACCOUNT_DISABLED_STATE))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        String valueStr = a.getValue();
        if (valueStr.equalsIgnoreCase("true"))
        {
          opElements.add(encode(OP_SET_ACCOUNT_DISABLED_STATE, "true"));
        }
        else if (valueStr.equalsIgnoreCase("false"))
        {
          opElements.add(encode(OP_SET_ACCOUNT_DISABLED_STATE, "false"));
        }
        else
        {
          msgID = MSGID_PWPSTATE_INVALID_BOOLEAN_VALUE;
          String message = getMessage(msgID, valueStr);
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
        }
      }
      else
      {
        msgID = MSGID_PWPSTATE_NO_BOOLEAN_VALUE;
        String message = getMessage(msgID);
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
      }
    }
    else if (subCommandName.equals(SC_CLEAR_ACCOUNT_DISABLED_STATE))
    {
      opElements.add(encode(OP_CLEAR_ACCOUNT_DISABLED_STATE, NO_VALUE));
    }
    else if (subCommandName.equals(SC_GET_ACCOUNT_EXPIRATION_TIME))
    {
      opElements.add(encode(OP_GET_ACCOUNT_EXPIRATION_TIME, NO_VALUE));
    }
    else if (subCommandName.equals(SC_SET_ACCOUNT_EXPIRATION_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_SET_ACCOUNT_EXPIRATION_TIME, a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_SET_ACCOUNT_EXPIRATION_TIME, NO_VALUE));
      }
    }
    else if (subCommandName.equals(SC_CLEAR_ACCOUNT_EXPIRATION_TIME))
    {
      opElements.add(encode(OP_CLEAR_ACCOUNT_EXPIRATION_TIME, NO_VALUE));
    }
    else if (subCommandName.equals(SC_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION, NO_VALUE));
    }
    else if (subCommandName.equals(SC_GET_PASSWORD_CHANGED_TIME))
    {
      opElements.add(encode(OP_GET_PASSWORD_CHANGED_TIME, NO_VALUE));
    }
    else if (subCommandName.equals(SC_SET_PASSWORD_CHANGED_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_SET_PASSWORD_CHANGED_TIME, a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_SET_PASSWORD_CHANGED_TIME, NO_VALUE));
      }
    }
    else if (subCommandName.equals(SC_CLEAR_PASSWORD_CHANGED_TIME))
    {
      opElements.add(encode(OP_CLEAR_PASSWORD_CHANGED_TIME, NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_PASSWORD_EXP_WARNED_TIME))
    {
      opElements.add(encode(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME, NO_VALUE));
    }
    else if(subCommandName.equals(SC_SET_PASSWORD_EXP_WARNED_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_SET_PASSWORD_EXPIRATION_WARNED_TIME,
                              a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_SET_PASSWORD_EXPIRATION_WARNED_TIME,
                              NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_CLEAR_PASSWORD_EXP_WARNED_TIME))
    {
      opElements.add(encode(OP_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME,
                            NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
                            NO_VALUE));
    }
    else if(subCommandName.equals(
                 SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
                            NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_AUTHENTICATION_FAILURE_TIMES))
    {
      opElements.add(encode(OP_GET_AUTHENTICATION_FAILURE_TIMES, NO_VALUE));
    }
    else if(subCommandName.equals(SC_ADD_AUTHENTICATION_FAILURE_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_ADD_AUTHENTICATION_FAILURE_TIME,
                              a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_ADD_AUTHENTICATION_FAILURE_TIME, NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_SET_AUTHENTICATION_FAILURE_TIMES))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        ArrayList<String> valueList = new ArrayList<String>(a.getValues());
        String[] values = new String[valueList.size()];
        valueList.toArray(values);
        opElements.add(encode(OP_SET_AUTHENTICATION_FAILURE_TIMES, values));
      }
      else
      {
        opElements.add(encode(OP_SET_AUTHENTICATION_FAILURE_TIMES, NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_CLEAR_AUTHENTICATION_FAILURE_TIMES))
    {
      opElements.add(encode(OP_CLEAR_AUTHENTICATION_FAILURE_TIMES, NO_VALUE));
    }
    else if(subCommandName.equals(
                 SC_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
                            NO_VALUE));
    }
    else if(subCommandName.equals(
                 SC_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT))
    {
      opElements.add(encode(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
                            NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_LAST_LOGIN_TIME))
    {
      opElements.add(encode(OP_GET_LAST_LOGIN_TIME, NO_VALUE));
    }
    else if(subCommandName.equals(SC_SET_LAST_LOGIN_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_SET_LAST_LOGIN_TIME, a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_SET_LAST_LOGIN_TIME, NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_CLEAR_LAST_LOGIN_TIME))
    {
      opElements.add(encode(OP_CLEAR_LAST_LOGIN_TIME, NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT, NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_PASSWORD_RESET_STATE))
    {
      opElements.add(encode(OP_GET_PASSWORD_RESET_STATE, NO_VALUE));
    }
    else if(subCommandName.equals(SC_SET_PASSWORD_RESET_STATE))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        String valueStr = a.getValue();
        if (valueStr.equalsIgnoreCase("true"))
        {
          opElements.add(encode(OP_SET_PASSWORD_RESET_STATE, "true"));
        }
        else if (valueStr.equalsIgnoreCase("false"))
        {
          opElements.add(encode(OP_SET_PASSWORD_RESET_STATE, "false"));
        }
        else
        {
          msgID = MSGID_PWPSTATE_INVALID_BOOLEAN_VALUE;
          String message = getMessage(msgID, valueStr);
          err.println(wrapText(message, MAX_LINE_WIDTH));
          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
        }
      }
      else
      {
        msgID = MSGID_PWPSTATE_NO_BOOLEAN_VALUE;
        String message = getMessage(msgID);
        err.println(wrapText(message, MAX_LINE_WIDTH));
        return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
      }
    }
    else if(subCommandName.equals(SC_CLEAR_PASSWORD_RESET_STATE))
    {
      opElements.add(encode(OP_GET_PASSWORD_RESET_STATE, NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
                            NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_GRACE_LOGIN_USE_TIMES))
    {
      opElements.add(encode(OP_GET_GRACE_LOGIN_USE_TIMES, NO_VALUE));
    }
    else if(subCommandName.equals(SC_ADD_GRACE_LOGIN_USE_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_ADD_GRACE_LOGIN_USE_TIME, a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_ADD_GRACE_LOGIN_USE_TIME, NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_SET_GRACE_LOGIN_USE_TIMES))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        ArrayList<String> valueList = new ArrayList<String>(a.getValues());
        String[] values = new String[valueList.size()];
        valueList.toArray(values);
        opElements.add(encode(OP_SET_GRACE_LOGIN_USE_TIMES, values));
      }
      else
      {
        opElements.add(encode(OP_SET_GRACE_LOGIN_USE_TIMES, NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_CLEAR_GRACE_LOGIN_USE_TIMES))
    {
      opElements.add(encode(OP_CLEAR_GRACE_LOGIN_USE_TIMES, NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_REMAINING_GRACE_LOGIN_COUNT))
    {
      opElements.add(encode(OP_GET_REMAINING_GRACE_LOGIN_COUNT, NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME))
    {
      opElements.add(encode(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                            NO_VALUE));
    }
    else if(subCommandName.equals(SC_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME))
    {
      Argument a = subCommand.getArgumentForName(ARG_OP_VALUE);
      if ((a != null) && a.isPresent())
      {
        opElements.add(encode(OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                              a.getValue()));
      }
      else
      {
        opElements.add(encode(OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                              NO_VALUE));
      }
    }
    else if(subCommandName.equals(SC_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME))
    {
      opElements.add(encode(OP_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME,
                            NO_VALUE));
    }
    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME))
    {
      opElements.add(encode(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
                            NO_VALUE));
    }
    else
    {
      msgID = MSGID_PWPSTATE_INVALID_SUBCOMMAND;
      String message = getMessage(msgID, subCommandName);
      err.println(wrapText(message, MAX_LINE_WIDTH));
      err.println(argParser.getUsage());
      return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
    }
    return LDAPResultCode.SUCCESS;
  }
  /**
   * Prints information about a password policy state variable to standard
   * output.
   *
   * @param  msgID   The message ID for the message to use as the label.
   * @param  values  The set of values for the associated state variable.
   */
  private static void printLabelAndValues(int msgID, ArrayList<String> values)
  {
    String label = getMessage(msgID);
    if ((values == null) || values.isEmpty())
    {
      out.print(label);
      out.println(":");
    }
    else
    {
      for (String value : values)
      {
        out.print(label);
        out.print(":  ");
        out.println(value);
      }
    }
  }
}
opends/src/server/org/opends/server/util/ServerConstants.java
@@ -695,6 +695,15 @@
  /**
   * The OID for the password policy state extended operation (both the request
   * and response types).
   */
  public static final String OID_PASSWORD_POLICY_STATE_EXTOP =
       "1.3.6.1.4.1.26027.1.6.1";
  /**
   * The request OID for the StartTLS extended operation.
   */
  public static final String OID_START_TLS_REQUEST = "1.3.6.1.4.1.1466.20037";
opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
@@ -45,6 +45,7 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.tools.LDAPModify;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -4742,6 +4743,85 @@
  /**
   * Tests the ability of a user to bind to the server when their account
   * includes the pwdReset operational attribute and last login time tracking is
   * enabled.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testResetWithLastLoginTime()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: oldpassword",
      "ds-privilege-name: bypass-acl"
    );
    try
    {
      TestCaseUtils.applyModifications(
        "dn: cn=Default Password Policy,cn=Password Policies,cn=config",
        "changetype: modify",
        "replace: ds-cfg-force-change-on-reset",
        "ds-cfg-force-change-on-reset: true",
        "-",
        "replace: ds-cfg-last-login-time-attribute",
        "ds-cfg-last-login-time-attribute: ds-pwp-last-login-time",
        "-",
        "replace: ds-cfg-last-login-time-format",
        "ds-cfg-last-login-time-format: yyyyMMdd",
        "",
        "dn: uid=test.user,o=test",
        "changetype: modify",
        "replace: userPassword",
        "userPassword: newpassword");
      String path = TestCaseUtils.createTempFile(
        "dn: uid=test.user,o=test",
        "changetype: modify",
        "replace: userPassword",
        "userPassword: newnewpassword"
      );
      String[] args =
      {
        "-h", "127.0.0.1",
        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-D", "uid=test.user,o=test",
        "-w", "newpassword",
        "-f", path
      };
      assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    }
    finally
    {
      TestCaseUtils.applyModifications(
        "dn: cn=Default Password Policy,cn=Password Policies,cn=config",
        "changetype: modify",
        "replace: ds-cfg-force-change-on-reset",
        "ds-cfg-force-change-on-reset: false",
        "-",
        "replace: ds-cfg-last-login-time-attribute",
        "-",
        "replace: ds-cfg-last-login-time-format");
    }
  }
  /**
   * Tests the <CODE>toString</CODE> methods with the default password policy.
   */
  @Test()
opends/tests/unit-tests-testng/src/server/org/opends/server/tools/ManageAccountTestCase.java
New file
@@ -0,0 +1,871 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.schema.GeneralizedTimeSyntax;
import static org.testng.Assert.*;
/**
 * A set of test cases for the ManageAccount tool.
 */
public class ManageAccountTestCase
       extends ToolsTestCase
{
  /**
   * Ensures that the Directory Server is running before starting any of the
   * tests.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Retrieves the names of all of all the subcommands available for use with
   * the manage-account tool.
   *
   * @return  The names of all of the subcommands available for use with the
   *          manage-account tool.
   */
  @DataProvider(name = "allSubCommands")
  public Object[][] getAllSubCommands()
  {
    return new Object[][]
    {
      new Object[] { "get-all" },
      new Object[] { "get-password-policy-dn" },
      new Object[] { "get-account-is-disabled" },
      new Object[] { "set-account-is-disabled" },
      new Object[] { "clear-account-is-disabled" },
      new Object[] { "get-account-expiration-time" },
      new Object[] { "set-account-expiration-time" },
      new Object[] { "clear-account-expiration-time" },
      new Object[] { "get-seconds-until-account-expiration" },
      new Object[] { "get-password-changed-time" },
      new Object[] { "set-password-changed-time" },
      new Object[] { "clear-password-changed-time" },
      new Object[] { "get-password-expiration-warned-time" },
      new Object[] { "set-password-expiration-warned-time" },
      new Object[] { "clear-password-expiration-warned-time" },
      new Object[] { "get-seconds-until-password-expiration" },
      new Object[] { "get-seconds-until-password-expiration-warning" },
      new Object[] { "get-authentication-failure-times" },
      new Object[] { "add-authentication-failure-time" },
      new Object[] { "set-authentication-failure-times" },
      new Object[] { "clear-authentication-failure-times" },
      new Object[] { "get-seconds-until-authentication-failure-unlock" },
      new Object[] { "get-remaining-authentication-failure-count" },
      new Object[] { "get-last-login-time" },
      new Object[] { "set-last-login-time" },
      new Object[] { "clear-last-login-time" },
      new Object[] { "get-seconds-until-idle-lockout" },
      new Object[] { "get-password-is-reset" },
      new Object[] { "set-password-is-reset" },
      new Object[] { "clear-password-is-reset" },
      new Object[] { "get-seconds-until-password-reset-lockout" },
      new Object[] { "get-grace-login-use-times" },
      new Object[] { "add-grace-login-use-time" },
      new Object[] { "set-grace-login-use-times" },
      new Object[] { "clear-grace-login-use-times" },
      new Object[] { "get-remaining-grace-login-count" },
      new Object[] { "get-password-changed-by-required-time" },
      new Object[] { "set-password-changed-by-required-time" },
      new Object[] { "clear-password-changed-by-required-time" },
      new Object[] { "get-seconds-until-required-change-time" }
    };
  }
  /**
   * Retrieves the names of all of the subcommands used for performing "get"
   * operations.
   *
   * @return  The names of all of the subcommands used for performing "get"
   *          operations.
   */
  @DataProvider(name = "getSubCommands")
  public Object[][] getGetSubCommands()
  {
    return new Object[][]
    {
      new Object[] { "get-all" },
      new Object[] { "get-password-policy-dn" },
      new Object[] { "get-account-is-disabled" },
      new Object[] { "get-account-expiration-time" },
      new Object[] { "get-seconds-until-account-expiration" },
      new Object[] { "get-password-changed-time" },
      new Object[] { "get-password-expiration-warned-time" },
      new Object[] { "get-seconds-until-password-expiration" },
      new Object[] { "get-seconds-until-password-expiration-warning" },
      new Object[] { "get-authentication-failure-times" },
      new Object[] { "get-seconds-until-authentication-failure-unlock" },
      new Object[] { "get-remaining-authentication-failure-count" },
      new Object[] { "get-last-login-time" },
      new Object[] { "get-seconds-until-idle-lockout" },
      new Object[] { "get-password-is-reset" },
      new Object[] { "get-seconds-until-password-reset-lockout" },
      new Object[] { "get-grace-login-use-times" },
      new Object[] { "get-remaining-grace-login-count" },
      new Object[] { "get-password-changed-by-required-time" },
      new Object[] { "get-seconds-until-required-change-time" }
    };
  }
  /**
   * Retrieves the names of the subcommands that may be used to set a Boolean
   * value in the user's password policy state.
   *
   * @return  The names of all of the subcommands that may be used to set a
   *          Boolean value in the user's password policy state.
   */
  @DataProvider(name = "setBooleanSubCommands")
  public Object[][] getSetBooleanSubCommands()
  {
    return new Object[][]
    {
      new Object[] { "set-account-is-disabled" },
      new Object[] { "set-password-is-reset" },
    };
  }
  /**
   * Retrieves the names of the subcommands that may be used to set time value
   * in the user's password policy state.  This will also include the
   * subcommands that may be used to add a value to a multivalued property.
   *
   * @return  The names of all of the subcommands that may be used to set a
   *          time value in the user's password policy state.
   */
  @DataProvider(name = "setTimeSubCommands")
  public Object[][] getSetTimeSubCommands()
  {
    return new Object[][]
    {
      new Object[] { "set-account-expiration-time" },
      new Object[] { "set-password-changed-time" },
      new Object[] { "set-password-expiration-warned-time" },
      new Object[] { "set-authentication-failure-times" },
      new Object[] { "add-authentication-failure-time" },
      new Object[] { "set-last-login-time" },
      new Object[] { "set-grace-login-use-times" },
      new Object[] { "add-grace-login-use-time" },
      new Object[] { "set-password-changed-by-required-time" },
    };
  }
  /**
   * Retrieves the names of all of all the subcommands that may be used to clear
   * some part of the password policy state.
   *
   * @return  The names of all of the subcommands that may be used to clear some
   *          part of the password policy state.
   */
  @DataProvider(name = "clearSubCommands")
  public Object[][] clearAllSubCommands()
  {
    return new Object[][]
    {
      new Object[] { "clear-account-is-disabled" },
      new Object[] { "clear-account-expiration-time" },
      new Object[] { "clear-password-changed-time" },
      new Object[] { "clear-password-expiration-warned-time" },
      new Object[] { "clear-authentication-failure-times" },
      new Object[] { "clear-last-login-time" },
      new Object[] { "clear-password-is-reset" },
      new Object[] { "clear-grace-login-use-times" },
      new Object[] { "clear-password-changed-by-required-time" }
    };
  }
  /**
   * Tests the various sets of arguments that may be used to get usage
   * information when no subcommand is given.
   */
  @Test()
  public void testHelpNoSubCommand()
  {
    assertEquals(ManageAccount.main(new String[] { "-H" }, null, System.err),
                 0);
    assertEquals(ManageAccount.main(new String[] { "--help" }, null,
                                    System.err),
                 0);
    assertEquals(ManageAccount.main(new String[] { "-?" }, null, System.err),
                 0);
  }
  /**
   * Tests the various sets of arguments that may be used to get usage
   * information when a subcommand is given.
   *
   * @param  subCommand  The subcommand to use in the test.
   */
  @Test(dataProvider="allSubCommands")
  public void testHelpWithSubCommand(String subCommand)
  {
    String[] args =
    {
      subCommand,
      "--help"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests the manage-account tool without any subcommand.
   */
  @Test()
  public void testNoSubCommand()
  {
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertFalse(ManageAccount.main(args, null, null) == 0);
  }
  /**
   * Tests the manage-account tool with an invalid subcommand.
   */
  @Test()
  public void testInvalidSubCommand()
  {
    String[] args =
    {
      "invalid-subcommand",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertFalse(ManageAccount.main(args, null, null) == 0);
  }
  /**
   * Tests an attempt to use the manage-account tool as an anonymous user.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testAnonymousUser()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      "get-all",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "",
      "-w", "",
      "-b", "uid=test.user,o=test"
    };
    assertFalse(ManageAccount.main(args, null, System.err) == 0);
  }
  /**
   * Tests an attempt to use the manage-account tool as an unprivileged user.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testUnprivilegedUser()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      "get-all",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "uid=test.user,o=test",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertFalse(ManageAccount.main(args, null, System.err) == 0);
  }
  /**
   * Tests the ability to use the manage-account tool when using SSL.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testUsingSSL()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      "get-all",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
      "-Z",
      "-X",
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests the ability to use the manage-account tool when using StartTLS.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testUsingStartTLS()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      "get-all",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-q",
      "-X",
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests the ability to use the manage-account tool when using SASL
   * authentication.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testUsingSASL()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      "get-all",
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-o", "mech=PLAIN",
      "-o", "authid=dn:cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests to ensure that the various "get" subcommands work without throwing
   * exceptions or returning unexpected results.
   *
   * @param  subCommand  The name of the "get" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="getSubCommands")
  public void testGetSubCommands(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests to ensure that the various "get" subcommands fail when provided with
   * a value.
   *
   * @param  subCommand  The name of the "get" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="getSubCommands")
  public void testGetSubCommandsWithValue(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
      "-O", "not-appropriate-for-this-subcommand"
    };
    assertFalse(ManageAccount.main(args, null, System.err) == 0);
  }
  /**
   * Tests to ensure that the various "set" subcommands that take Boolean
   * arguments work properly when given a value of "true".
   *
   * @param  subCommand  The name of the "set" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="setBooleanSubCommands")
  public void testSetBooleanSubCommandsTrue(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
      "-O", "true"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests to ensure that the various "set" subcommands that take Boolean
   * arguments work properly when given a value of "false".
   *
   * @param  subCommand  The name of the "set" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="setBooleanSubCommands")
  public void testSetBooleanSubCommandsFalse(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
      "-O", "false"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests to ensure that the various "set" subcommands that take Boolean
   * arguments work properly when given a non-Boolean value.
   *
   * @param  subCommand  The name of the "set" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="setBooleanSubCommands")
  public void testSetBooleanSubCommandsNonBooleanValue(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
      "-O", "nonboolean"
    };
    assertFalse(ManageAccount.main(args, null, System.err) == 0);
  }
  /**
   * Tests to ensure that the various "set" subcommands that take timestamp
   * arguments work properly when used without any value.
   *
   * @param  subCommand  The name of the "set" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="setTimeSubCommands")
  public void testSetTimeSubCommandsNoValue(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test"
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests to ensure that the various "set" subcommands that take timestamp
   * arguments work properly when used with a value equal to the current time.
   *
   * @param  subCommand  The name of the "set" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="setTimeSubCommands")
  public void testSetTimeSubCommandsCurrentTime(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
      "-O", GeneralizedTimeSyntax.format(System.currentTimeMillis())
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
  /**
   * Tests to ensure that the various "set" subcommands that take timestamp
   * arguments work properly when used with an invalid value.
   *
   * @param  subCommand  The name of the "set" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="setTimeSubCommands")
  public void testSetTimeSubCommandsInvalidTime(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
      "-O", "invalid"
    };
    assertFalse(ManageAccount.main(args, null, System.err) == 0);
  }
  /**
   * Tests to ensure that the various "clear" subcommands work without throwing
   * exceptions or returning unexpected results.
   *
   * @param  subCommand  The name of the "clear" subcommand to invoke.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider="clearSubCommands")
  public void testClearSubCommands(String subCommand)
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    TestCaseUtils.addEntry(
      "dn: uid=test.user,o=test",
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password"
    );
    String[] args =
    {
      subCommand,
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-b", "uid=test.user,o=test",
    };
    assertEquals(ManageAccount.main(args, null, System.err), 0);
  }
}