From 974a3ba348bab7b304d1b2d007ef14bc49e7dff7 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 14 Aug 2006 13:33:54 +0000
Subject: [PATCH] Make a few changes in the area of account status notifications:

---
 opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java                            |   26 +
 opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java                                  |  188 ++++++++++
 opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotificationType.java                 |   95 +++++
 opendj-sdk/opends/src/server/org/opends/server/api/AccountStatusNotificationHandler.java                |    6 
 opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java                              |    9 
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPConnection.java                                |    3 
 opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java                               |   41 ++
 opendj-sdk/opends/resource/config/config.ldif                                                           |   20 +
 opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java                         |   61 +++
 opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java                            |    2 
 opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotification.java                     |   28 +
 opendj-sdk/opends/resource/schema/02-config.ldif                                                        |   11 
 opendj-sdk/opends/src/server/org/opends/server/extensions/ErrorLogAccountStatusNotificationHandler.java |  414 +++++++++++++++++++++++++
 13 files changed, 881 insertions(+), 23 deletions(-)

diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index 7b9362c..52d2fa0 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -55,6 +55,26 @@
 objectClass: ds-cfg-branch
 cn: Account Status Notification Handlers
 
+dn: cn=Error Log Handler,cn=Account Status Notification Handlers,cn=config
+objectClass: top
+objectClass: ds-cfg-account-status-notification-handler
+objectClass: ds-cfg-error-log-account-status-notification-handler
+cn: Error Log Handler
+ds-cfg-account-status-notification-handler-class: org.opends.server.extensions.ErrorLogAccountStatusNotificationHandler
+ds-cfg-account-status-notification-handler-enabled: true
+ds-cfg-account-status-notification-type: account-temporarily-locked
+ds-cfg-account-status-notification-type: account-permanently-locked
+ds-cfg-account-status-notification-type: account-unlocked
+ds-cfg-account-status-notification-type: account-idle-locked
+ds-cfg-account-status-notification-type: account-reset-locked
+ds-cfg-account-status-notification-type: account-disabled
+ds-cfg-account-status-notification-type: account-enabled
+ds-cfg-account-status-notification-type: account-expired
+ds-cfg-account-status-notification-type: password-expired
+ds-cfg-account-status-notification-type: password-expiring
+ds-cfg-account-status-notification-type: password-reset
+ds-cfg-account-status-notification-type: password-changed
+
 dn: cn=Alert Handlers,cn=config
 objectClass: top
 objectClass: ds-cfg-branch
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 03b608a..1567cc6 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -967,6 +967,9 @@
   NAME 'ds-cfg-account-status-notification-handler-enabled'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.283
+  NAME 'ds-cfg-account-status-notification-type'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
   MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1322,7 +1325,13 @@
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.75
   NAME 'ds-cfg-account-status-notification-handler' SUP top STRUCTURAL
-  MUST ( ds-cfg-account-status-notification-handler-class $
+  MUST ( cn $ ds-cfg-account-status-notification-handler-class $
   ds-cfg-account-status-notification-handler-enabled )
   X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.76
+  NAME 'ds-cfg-error-log-account-status-notification-handler'
+  SUP ds-cfg-account-status-notification-handler STRUCTURAL
+  MUST ds-cfg-account-status-notification-type
+  X-ORIGIN 'OpenDS Directory Server' )
+
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/AccountStatusNotificationHandler.java b/opendj-sdk/opends/src/server/org/opends/server/api/AccountStatusNotificationHandler.java
index ec48c6e..c221c07 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/AccountStatusNotificationHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/AccountStatusNotificationHandler.java
@@ -33,6 +33,7 @@
 import org.opends.server.core.InitializationException;
 import org.opends.server.types.AccountStatusNotification;
 import org.opends.server.types.AccountStatusNotificationType;
+import org.opends.server.types.DN;
 
 import static org.opends.server.loggers.Debug.*;
 
@@ -99,6 +100,8 @@
    *
    * @param  notificationType  The type for this account status
    *                           notification.
+   * @param  userDN            The DN of the user entry to which this
+   *                           notification applies.
    * @param  messageID         The unique ID for this notification.
    * @param  message           The human-readable message for this
    *                           notification.
@@ -106,7 +109,7 @@
   public abstract void
        handleStatusNotification(
             AccountStatusNotificationType notificationType,
-            int messageID, String message);
+            DN userDN, int messageID, String message);
 
 
 
@@ -124,6 +127,7 @@
                       String.valueOf(notification));
 
     handleStatusNotification(notification.getNotificationType(),
+                             notification.getUserDN(),
                              notification.getMessageID(),
                              notification.getMessage());
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
index 226ed2a..4da841b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -124,6 +124,15 @@
 
 
   /**
+   * The name of the configuration attribute that specifies the set of account
+   * status notification types that should trigger notifications.
+   */
+  public static final String ATTR_ACCT_NOTIFICATION_TYPE =
+       NAME_PREFIX_CFG + "account-status-notification-type";
+
+
+
+  /**
    * The name of the configuration attribute that indicates whether to
    * automatically add missing RDN attributes or to return an error response to
    * the client.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java
index d621ec1..1769d59 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java
@@ -46,6 +46,7 @@
 import org.opends.server.controls.PasswordPolicyResponseControl;
 import org.opends.server.controls.PasswordPolicyWarningType;
 import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.types.AccountStatusNotificationType;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
@@ -128,6 +129,9 @@
   // The bind DN used for this bind operation.
   private DN bindDN;
 
+  // The DN of the user entry that is attempting to authenticate.
+  private DN userEntryDN;
+
   // The DN of the user as whom a SASL authentication was attempted (regardless
   // of whether the authentication was successful) for the purpose of updating
   // password policy state information.
@@ -205,6 +209,7 @@
     this.saslCredentials = null;
 
     bindDN                   = null;
+    userEntryDN              = null;
     responseControls         = new ArrayList<Control>(0);
     authFailureID            = 0;
     authFailureReason        = null;
@@ -261,6 +266,7 @@
     this.simplePassword  = null;
 
     bindDN            = null;
+    userEntryDN       = null;
     responseControls  = new ArrayList<Control>(0);
     authFailureID     = 0;
     authFailureReason = null;
@@ -324,6 +330,7 @@
     pwPolicyErrorType        = null;
     pwPolicyWarningType      = null;
     pwPolicyWarningValue     = -1;
+    userEntryDN              = null;
   }
 
 
@@ -379,6 +386,7 @@
     authFailureID     = 0;
     authFailureReason = null;
     saslAuthUserEntry = null;
+    userEntryDN       = null;
   }
 
 
@@ -701,6 +709,24 @@
 
 
   /**
+   * Retrieves the user entry DN for this bind operation.  It will only be
+   * available if the bind processing has proceeded far enough to identify the
+   * user attempting to authenticate or if the user DN could not be determined.
+   *
+   * @return  The user entry DN for this bind operation, or <CODE>null</CODE> if
+   *          the bind processing has not progressed far enough to identify the
+   *          user or if the user DN could not be determined.
+   */
+  public DN getUserEntryDN()
+  {
+    assert debugEnter(CLASS_NAME, "getUserEntryDN");
+
+    return userEntryDN;
+  }
+
+
+
+  /**
    * Retrieves the time that processing started for this operation.
    *
    * @return  The time that processing started for this operation.
@@ -1122,6 +1148,10 @@
               setAuthFailureReason(msgID, message);
               break bindProcessing;
             }
+            else
+            {
+              userEntryDN = userEntry.getDN();
+            }
 
 
             // Check to see if the user has a password.  If not, then fail.
@@ -1172,6 +1202,11 @@
 
               setResultCode(ResultCode.INVALID_CREDENTIALS);
               setAuthFailureReason(msgID, message);
+
+              pwPolicyState.generateAccountStatusNotification(
+                   AccountStatusNotificationType.ACCOUNT_EXPIRED, bindDN, msgID,
+                   message);
+
               break bindProcessing;
             }
             else if (pwPolicyState.lockedDueToFailures())
@@ -1200,6 +1235,11 @@
 
               setResultCode(ResultCode.INVALID_CREDENTIALS);
               setAuthFailureReason(msgID, message);
+
+              pwPolicyState.generateAccountStatusNotification(
+                   AccountStatusNotificationType.ACCOUNT_RESET_LOCKED, bindDN,
+                   msgID, message);
+
               break bindProcessing;
             }
             else if (pwPolicyState.lockedDueToIdleInterval())
@@ -1214,6 +1254,11 @@
 
               setResultCode(ResultCode.INVALID_CREDENTIALS);
               setAuthFailureReason(msgID, message);
+
+              pwPolicyState.generateAccountStatusNotification(
+                   AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED, bindDN,
+                   msgID, message);
+
               break bindProcessing;
             }
 
@@ -1252,6 +1297,11 @@
 
                   setResultCode(ResultCode.INVALID_CREDENTIALS);
                   setAuthFailureReason(msgID, message);
+
+                  pwPolicyState.generateAccountStatusNotification(
+                       AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN,
+                       msgID, message);
+
                   break bindProcessing;
                 }
               }
@@ -1262,17 +1312,28 @@
 
                 setResultCode(ResultCode.INVALID_CREDENTIALS);
                 setAuthFailureReason(msgID, message);
+
+                pwPolicyState.generateAccountStatusNotification(
+                     AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN,
+                     msgID, message);
+
                 break bindProcessing;
               }
             }
             else if (pwPolicyState.shouldWarn())
             {
+              int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+              String timeToExpiration = secondsToTimeString(numSeconds);
+
+              int msgID = MSGID_BIND_PASSWORD_EXPIRING;
+              String message = getMessage(msgID, timeToExpiration);
+              appendErrorMessage(message);
+
               if (pwPolicyWarningType == null)
               {
                 pwPolicyWarningType =
                      PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION;
-                pwPolicyWarningValue =
-                     pwPolicyState.getSecondsUntilExpiration();
+                pwPolicyWarningValue = numSeconds;
               }
 
               isFirstWarning = pwPolicyState.isFirstWarning();
@@ -1418,6 +1479,16 @@
               if (isFirstWarning)
               {
                 pwPolicyState.setWarnedTime();
+
+                int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+                String timeToExpiration = secondsToTimeString(numSeconds);
+
+                int msgID = MSGID_BIND_PASSWORD_EXPIRING;
+                String message = getMessage(msgID, timeToExpiration);
+
+                pwPolicyState.generateAccountStatusNotification(
+                     AccountStatusNotificationType.PASSWORD_EXPIRING, bindDN,
+                     msgID, message);
               }
 
               if (isGraceLogin)
@@ -1439,10 +1510,32 @@
               if (maxAllowedFailures > 0)
               {
                 pwPolicyState.updateAuthFailureTimes();
-                if (pwPolicyState.getAuthFailureTimes().size() >
+                if (pwPolicyState.getAuthFailureTimes().size() >=
                     maxAllowedFailures)
                 {
                   pwPolicyState.lockDueToFailures();
+
+                  AccountStatusNotificationType notificationType;
+
+                  int lockoutDuration = pwPolicyState.getLockoutDuration();
+                  if (lockoutDuration > 0)
+                  {
+                    notificationType = AccountStatusNotificationType.
+                                            ACCOUNT_TEMPORARILY_LOCKED;
+                    msgID   = MSGID_BIND_ACCOUNT_TEMPORARILY_LOCKED;
+                    message = getMessage(msgID,
+                                         secondsToTimeString(lockoutDuration));
+                  }
+                  else
+                  {
+                    notificationType = AccountStatusNotificationType.
+                                            ACCOUNT_PERMANENTLY_LOCKED;
+                    msgID   = MSGID_BIND_ACCOUNT_PERMANENTLY_LOCKED;
+                    message = getMessage(msgID);
+                  }
+
+                  pwPolicyState.generateAccountStatusNotification(
+                       notificationType, userEntryDN, msgID, message);
                 }
               }
             }
@@ -1532,7 +1625,8 @@
               // FIXME -- Need to have a way to enable debugging.
               pwPolicyState = new PasswordPolicyState(saslAuthUserEntry, false,
                                                       false);
-              userDNString = String.valueOf(saslAuthUserEntry.getDN());
+              userEntryDN = saslAuthUserEntry.getDN();
+              userDNString = String.valueOf(userEntryDN);
             }
             catch (DirectoryException de)
             {
@@ -1560,8 +1654,14 @@
             {
               setResultCode(ResultCode.INVALID_CREDENTIALS);
 
-              int msgID = MSGID_BIND_OPERATION_ACCOUNT_EXPIRED;
-              appendErrorMessage(getMessage(msgID, userDNString));
+              int    msgID   = MSGID_BIND_OPERATION_ACCOUNT_EXPIRED;
+              String message = getMessage(msgID, userDNString);
+              appendErrorMessage(message);
+
+              pwPolicyState.generateAccountStatusNotification(
+                   AccountStatusNotificationType.ACCOUNT_EXPIRED, bindDN, msgID,
+                   message);
+
               break bindProcessing;
             }
 
@@ -1600,8 +1700,14 @@
                 pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
               }
 
-              int msgID = MSGID_BIND_OPERATION_ACCOUNT_IDLE_LOCKED;
-              appendErrorMessage(getMessage(msgID, userDNString));
+              int    msgID   = MSGID_BIND_OPERATION_ACCOUNT_IDLE_LOCKED;
+              String message = getMessage(msgID, userDNString);
+              appendErrorMessage(message);
+
+              pwPolicyState.generateAccountStatusNotification(
+                   AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED, bindDN,
+                   msgID, message);
+
               break bindProcessing;
             }
 
@@ -1617,8 +1723,14 @@
                   pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
                 }
 
-                int msgID = MSGID_BIND_OPERATION_ACCOUNT_RESET_LOCKED;
-                appendErrorMessage(getMessage(msgID, userDNString));
+                int    msgID   = MSGID_BIND_OPERATION_ACCOUNT_RESET_LOCKED;
+                String message = getMessage(msgID, userDNString);
+                appendErrorMessage(message);
+
+                pwPolicyState.generateAccountStatusNotification(
+                     AccountStatusNotificationType.ACCOUNT_RESET_LOCKED, bindDN,
+                     msgID, message);
+
                 break bindProcessing;
               }
 
@@ -1655,6 +1767,11 @@
 
                     setResultCode(ResultCode.INVALID_CREDENTIALS);
                     setAuthFailureReason(msgID, message);
+
+                    pwPolicyState.generateAccountStatusNotification(
+                         AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN,
+                         msgID, message);
+
                     break bindProcessing;
                   }
                 }
@@ -1665,17 +1782,28 @@
 
                   setResultCode(ResultCode.INVALID_CREDENTIALS);
                   setAuthFailureReason(msgID, message);
+
+                  pwPolicyState.generateAccountStatusNotification(
+                       AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN,
+                       msgID, message);
+
                   break bindProcessing;
                 }
               }
               else if (pwPolicyState.shouldWarn())
               {
+                int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+                String timeToExpiration = secondsToTimeString(numSeconds);
+
+                int msgID = MSGID_BIND_PASSWORD_EXPIRING;
+                String message = getMessage(msgID, timeToExpiration);
+                appendErrorMessage(message);
+
                 if (pwPolicyWarningType == null)
                 {
                   pwPolicyWarningType =
                        PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION;
-                  pwPolicyWarningValue =
-                       pwPolicyState.getSecondsUntilExpiration();
+                  pwPolicyWarningValue = numSeconds;
                 }
 
                 isFirstWarning = pwPolicyState.isFirstWarning();
@@ -1701,6 +1829,16 @@
               if (isFirstWarning)
               {
                 pwPolicyState.setWarnedTime();
+
+                int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+                String timeToExpiration = secondsToTimeString(numSeconds);
+
+                int msgID = MSGID_BIND_PASSWORD_EXPIRING;
+                String message = getMessage(msgID, timeToExpiration);
+
+                pwPolicyState.generateAccountStatusNotification(
+                     AccountStatusNotificationType.PASSWORD_EXPIRING, bindDN,
+                     msgID, message);
               }
 
               if (isGraceLogin)
@@ -1809,10 +1947,34 @@
                 if (maxAllowedFailures > 0)
                 {
                   pwPolicyState.updateAuthFailureTimes();
-                  if (pwPolicyState.getAuthFailureTimes().size() >
+                  if (pwPolicyState.getAuthFailureTimes().size() >=
                       maxAllowedFailures)
                   {
                     pwPolicyState.lockDueToFailures();
+
+                    AccountStatusNotificationType notificationType;
+                    int msgID;
+                    String message;
+
+                    int lockoutDuration = pwPolicyState.getLockoutDuration();
+                    if (lockoutDuration > 0)
+                    {
+                      notificationType = AccountStatusNotificationType.
+                                              ACCOUNT_TEMPORARILY_LOCKED;
+                      msgID   = MSGID_BIND_ACCOUNT_TEMPORARILY_LOCKED;
+                      message = getMessage(msgID,
+                                     secondsToTimeString(lockoutDuration));
+                    }
+                    else
+                    {
+                      notificationType = AccountStatusNotificationType.
+                                              ACCOUNT_PERMANENTLY_LOCKED;
+                      msgID   = MSGID_BIND_ACCOUNT_PERMANENTLY_LOCKED;
+                      message = getMessage(msgID);
+                    }
+
+                    pwPolicyState.generateAccountStatusNotification(
+                         notificationType, userEntryDN, msgID, message);
                   }
                 }
               }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java b/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
index 9518f50..ec810b2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
@@ -1770,6 +1770,23 @@
 
 
   /**
+   * Retrieves the length of time in seconds that a user's account will be
+   * locked due to failed attempts before it will be automatically unlocked.
+   *
+   * @return  The length of time in seconds that a user's account will be
+   *          locked due to failed attempts before it will be automatically
+   *          unlocked, or zero if accounts will not be automatically unlocked.
+   */
+  public int getLockoutDuration()
+  {
+    assert debugEnter(CLASS_NAME, "getLockoutDuration");
+
+    return passwordPolicy.getLockoutDuration();
+  }
+
+
+
+  /**
    * Retrieves the length of time in seconds until the user's account is
    * automatically unlocked.  This should only be called after calling
    * <CODE>lockedDueToFailures</CODE>.
@@ -4122,15 +4139,17 @@
    * Generates an account status notification for this user.
    *
    * @param  notificationType  The type for the account status notification.
+   * @param  userDN            The DN of the user entry to which this
+   *                           notification applies.
    * @param  messageID         The unique ID for the notification.
    * @param  message           The human-readable message for the notification.
    */
   public void generateAccountStatusNotification(
                    AccountStatusNotificationType notificationType,
-                   int messageID, String message)
+                   DN userDN, int messageID, String message)
   {
     assert debugEnter(CLASS_NAME, "generateAccountStatusNotification",
-                      String.valueOf(notificationType),
+                      String.valueOf(notificationType), String.valueOf(userDN),
                       String.valueOf(messageID), String.valueOf(message));
 
 
@@ -4143,7 +4162,8 @@
 
     for (AccountStatusNotificationHandler handler : handlers)
     {
-      handler.handleStatusNotification(notificationType, messageID, message);
+      handler.handleStatusNotification(notificationType, userDN, messageID,
+                                       message);
     }
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/ErrorLogAccountStatusNotificationHandler.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/ErrorLogAccountStatusNotificationHandler.java
new file mode 100644
index 0000000..9286f4f
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/ErrorLogAccountStatusNotificationHandler.java
@@ -0,0 +1,414 @@
+/*
+ * 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 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.server.api.AccountStatusNotificationHandler;
+import org.opends.server.api.ConfigurableComponent;
+import org.opends.server.config.ConfigAttribute;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.config.MultiChoiceConfigAttribute;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.InitializationException;
+import org.opends.server.types.AccountStatusNotificationType;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.types.ResultCode;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.loggers.Error.*;
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class defines an account status notification handler that will write
+ * information about status notifications using the Directory Server's error
+ * logging facility.
+ */
+public class ErrorLogAccountStatusNotificationHandler
+       extends AccountStatusNotificationHandler
+       implements ConfigurableComponent
+{
+  /**
+   * The fully-qualified name of this class for debugging purposes.
+   */
+  private static final String CLASS_NAME =
+       "org.opends.server.extensions.ErrorLogAccountStatusNotificationHandler";
+
+
+
+  /**
+   * The set of names for the account status notification types that may be
+   * logged by this notification handler.
+   */
+  private static final HashSet<String> NOTIFICATION_TYPE_NAMES =
+       new HashSet<String>();
+
+
+
+  // The DN of the configuration entry for this notification handler.
+  private DN configEntryDN;
+
+  // The set of notification types that should generate log messages.
+  private HashSet<AccountStatusNotificationType> notificationTypes;
+
+
+
+
+  static
+  {
+    for (AccountStatusNotificationType t :
+         AccountStatusNotificationType.values())
+    {
+      NOTIFICATION_TYPE_NAMES.add(t.getNotificationTypeName());
+    }
+  }
+
+
+
+  /**
+   * Initializes this account status notification handler based on the
+   * information in the provided configuration entry.
+   *
+   * @param  configEntry  The configuration entry that contains the information
+   *                      to use to initialize this account status notification
+   *                      handler.
+   *
+   * @throws  ConfigException  If the provided entry does not contain a valid
+   *                           configuration for this account status
+   *                           notification handler.
+   *
+   * @throws  InitializationException  If a problem occurs during initialization
+   *                                   that is not related to the server
+   *                                   configuration.
+   */
+  public void initializeStatusNotificationHandler(ConfigEntry configEntry)
+       throws ConfigException, InitializationException
+  {
+    assert debugEnter(CLASS_NAME, "initializeStatusNotificationHandler",
+                      String.valueOf(configEntry));
+
+
+    configEntryDN = configEntry.getDN();
+
+
+    // Initialize the set of notification types that should generate log
+    // messages.
+    int msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_DESCRIPTION_NOTIFICATION_TYPES;
+    MultiChoiceConfigAttribute typesStub =
+         new MultiChoiceConfigAttribute(ATTR_ACCT_NOTIFICATION_TYPE,
+                                        getMessage(msgID), true, true, false,
+                                        NOTIFICATION_TYPE_NAMES);
+    try
+    {
+      MultiChoiceConfigAttribute typesAttr =
+           (MultiChoiceConfigAttribute)
+           configEntry.getConfigAttribute(typesStub);
+      notificationTypes = new HashSet<AccountStatusNotificationType>();
+      for (String s : typesAttr.activeValues())
+      {
+        AccountStatusNotificationType t =
+             AccountStatusNotificationType.typeForName(s);
+        if (t == null)
+        {
+          msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_INVALID_TYPE;
+          String message = getMessage(msgID, String.valueOf(configEntryDN), s);
+          throw new ConfigException(msgID, message);
+        }
+        else
+        {
+          notificationTypes.add(t);
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "initializeStatusNotificationHandler",
+                            e);
+
+      msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_CANNOT_GET_NOTIFICATION_TYPES;
+      String message = getMessage(msgID, String.valueOf(configEntryDN),
+                                  stackTraceToSingleLineString(e));
+      throw new InitializationException(msgID, message, e);
+    }
+
+
+    DirectoryServer.registerConfigurableComponent(this);
+    DirectoryServer.registerAccountStatusNotificationHandler(configEntryDN,
+                                                             this);
+  }
+
+
+
+  /**
+   * Performs any processing that may be necessary in conjunction with the
+   * provided account status notification type.
+   *
+   * @param  notificationType  The type for this account status notification.
+   * @param  userDN            The DN of the user entry to which this
+   *                           notification applies.
+   * @param  messageID         The unique ID for this notification.
+   * @param  message           The human-readable message for this notification.
+   */
+  public void handleStatusNotification(AccountStatusNotificationType
+                                            notificationType,
+                                       DN userDN, int messageID, String message)
+  {
+    assert debugEnter(CLASS_NAME, "handleStatusNotification",
+                      String.valueOf(notificationType), String.valueOf(userDN),
+                      String.valueOf(messageID), String.valueOf(message));
+
+    if (notificationTypes.contains(notificationType))
+    {
+      int msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_NOTIFICATION;
+      logError(ErrorLogCategory.PASSWORD_POLICY, ErrorLogSeverity.NOTICE,
+               msgID, notificationType.getNotificationTypeName(),
+               String.valueOf(userDN), messageID, message);
+    }
+  }
+
+
+
+  /**
+   * Retrieves the DN of the configuration entry with which this component is
+   * associated.
+   *
+   * @return  The DN of the configuration entry with which this component is
+   *          associated.
+   */
+  public DN getConfigurableComponentEntryDN()
+  {
+    assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
+
+    return configEntryDN;
+  }
+
+
+
+  /**
+   * Retrieves the set of configuration attributes that are associated with this
+   * configurable component.
+   *
+   * @return  The set of configuration attributes that are associated with this
+   *          configurable component.
+   */
+  public List<ConfigAttribute> getConfigurationAttributes()
+  {
+    assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
+
+
+    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
+
+    LinkedList<String> typeNames = new LinkedList<String>();
+    for (AccountStatusNotificationType t : notificationTypes)
+    {
+      typeNames.add(t.getNotificationTypeName());
+    }
+
+    int msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_DESCRIPTION_NOTIFICATION_TYPES;
+    attrList.add(new MultiChoiceConfigAttribute(ATTR_ACCT_NOTIFICATION_TYPE,
+                                                getMessage(msgID), true, true,
+                                                false, NOTIFICATION_TYPE_NAMES,
+                                                typeNames));
+
+    return attrList;
+  }
+
+
+
+  /**
+   * Indicates whether the provided configuration entry has an
+   * acceptable configuration for this component.  If it does not,
+   * then detailed information about the problem(s) should be added to
+   * the provided list.
+   *
+   * @param  configEntry          The configuration entry for which to
+   *                              make the determination.
+   * @param  unacceptableReasons  A list that can be used to hold
+   *                              messages about why the provided
+   *                              entry does not have an acceptable
+   *                              configuration.
+   *
+   * @return  <CODE>true</CODE> if the provided entry has an
+   *          acceptable configuration for this component, or
+   *          <CODE>false</CODE> if not.
+   */
+  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
+                      List<String> unacceptableReasons)
+  {
+    assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
+                      String.valueOf(configEntry), "List<String>");
+
+
+    // Initialize the set of notification types that should generate log
+    // messages.
+    int msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_DESCRIPTION_NOTIFICATION_TYPES;
+    MultiChoiceConfigAttribute typesStub =
+         new MultiChoiceConfigAttribute(ATTR_ACCT_NOTIFICATION_TYPE,
+                                        getMessage(msgID), true, true, false,
+                                        NOTIFICATION_TYPE_NAMES);
+    try
+    {
+      MultiChoiceConfigAttribute typesAttr =
+           (MultiChoiceConfigAttribute)
+           configEntry.getConfigAttribute(typesStub);
+      HashSet<AccountStatusNotificationType> types =
+           new HashSet<AccountStatusNotificationType>();
+      for (String s : typesAttr.activeValues())
+      {
+        AccountStatusNotificationType t =
+             AccountStatusNotificationType.typeForName(s);
+        if (t == null)
+        {
+          msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_INVALID_TYPE;
+          String message = getMessage(msgID, String.valueOf(configEntryDN), s);
+          unacceptableReasons.add(message);
+          return false;
+        }
+        else
+        {
+          types.add(t);
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
+
+      msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_CANNOT_GET_NOTIFICATION_TYPES;
+      String message = getMessage(msgID, String.valueOf(configEntryDN),
+                                  stackTraceToSingleLineString(e));
+      unacceptableReasons.add(message);
+      return false;
+    }
+
+
+    // If we've gotten here, then everything is OK.
+    return true;
+  }
+
+
+
+  /**
+   * Makes a best-effort attempt to apply the configuration contained
+   * in the provided entry.  Information about the result of this
+   * processing should be added to the provided message list.
+   * Information should always be added to this list if a
+   * configuration change could not be applied.  If detailed results
+   * are requested, then information about the changes applied
+   * successfully (and optionally about parameters that were not
+   * changed) should also be included.
+   *
+   * @param  configEntry      The entry containing the new
+   *                          configuration to apply for this
+   *                          component.
+   * @param  detailedResults  Indicates whether detailed information
+   *                          about the processing should be added to
+   *                          the list.
+   *
+   * @return  Information about the result of the configuration
+   *          update.
+   */
+  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
+                                                  boolean detailedResults)
+  {
+    assert debugEnter(CLASS_NAME, "applyNewConfiguration",
+                      String.valueOf(configEntry),
+                      String.valueOf(detailedResults));
+
+
+    ResultCode resultCode = ResultCode.SUCCESS;
+    boolean adminActionRequired = false;
+    ArrayList<String> messages = new ArrayList<String>();
+
+
+    // Initialize the set of notification types that should generate log
+    // messages.
+    HashSet<AccountStatusNotificationType> types =
+         new HashSet<AccountStatusNotificationType>();
+    int msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_DESCRIPTION_NOTIFICATION_TYPES;
+    MultiChoiceConfigAttribute typesStub =
+         new MultiChoiceConfigAttribute(ATTR_ACCT_NOTIFICATION_TYPE,
+                                        getMessage(msgID), true, true, false,
+                                        NOTIFICATION_TYPE_NAMES);
+    try
+    {
+      MultiChoiceConfigAttribute typesAttr =
+           (MultiChoiceConfigAttribute)
+           configEntry.getConfigAttribute(typesStub);
+      for (String s : typesAttr.activeValues())
+      {
+        AccountStatusNotificationType t =
+             AccountStatusNotificationType.typeForName(s);
+        if (t == null)
+        {
+          resultCode = ResultCode.UNWILLING_TO_PERFORM;
+
+          msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_INVALID_TYPE;
+          messages.add(getMessage(msgID, String.valueOf(configEntryDN), s));
+        }
+        else
+        {
+          types.add(t);
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
+
+      resultCode = DirectoryServer.getServerErrorResultCode();
+
+      msgID = MSGID_ERRORLOG_ACCTNOTHANDLER_CANNOT_GET_NOTIFICATION_TYPES;
+      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
+                              stackTraceToSingleLineString(e)));
+    }
+
+
+    if (resultCode == ResultCode.SUCCESS)
+    {
+      this.notificationTypes = types;
+    }
+
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index 9c690b4..0c749c4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5725,6 +5725,37 @@
 
 
   /**
+   * The message ID for the message that will be used if the user's password is
+   * about to expire.  This takes a single argument, which is the length of time
+   * until the password expires.
+   */
+  public static final int MSGID_BIND_PASSWORD_EXPIRING =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_WARNING | 547;
+
+
+
+  /**
+   * The message ID for the message that will be used if the user's account
+   * becomes temporarily locked due to too many failed attempts.  This takes a
+   * single argument, which is a string representation of the length of time
+   * until the account is unlocked.
+   */
+  public static final int MSGID_BIND_ACCOUNT_TEMPORARILY_LOCKED =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 548;
+
+
+
+  /**
+   * The message ID for the message that will be used if the user's account
+   * becomes permanently locked due to too many failed attempts.  This does not
+   * take any arguments.
+   */
+  public static final int MSGID_BIND_ACCOUNT_PERMANENTLY_LOCKED =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 549;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined
    * in this class.
    */
@@ -6513,9 +6544,19 @@
                     "The user-specific time limit value %s contained in " +
                     "user entry %s could not be parsed as an integer.  The " +
                     "default server time limit will be used.");
+    registerMessage(MSGID_BIND_PASSWORD_EXPIRING,
+                    "The user password is about to expire (time to " +
+                    "expiration:  %s).");
     registerMessage(MSGID_BIND_OPERATION_WRONG_PASSWORD,
                     "The password provided by the user did not match any " +
                     "password(s) stored in the user's entry.");
+    registerMessage(MSGID_BIND_ACCOUNT_TEMPORARILY_LOCKED,
+                    "The account has been locked as a result of too many " +
+                    "failed authentication attempts (time to unlock:  %s).");
+    registerMessage(MSGID_BIND_ACCOUNT_PERMANENTLY_LOCKED,
+                    "The account has been locked as a result of too many " +
+                    "failed authentication attempts.  It may only be " +
+                    "unlocked by an administrator.");
     registerMessage(MSGID_BIND_OPERATION_PASSWORD_VALIDATION_EXCEPTION,
                     "An unexpected error occurred while attempting to " +
                     "validate the provided password:  %s.");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index 183590d..7691fe1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -3928,6 +3928,50 @@
 
 
   /**
+   * The message ID for the message that will be used as the description for the
+   * notification types configuration attribute.  This does not take any
+   * arguments.
+   */
+  public static final int
+       MSGID_ERRORLOG_ACCTNOTHANDLER_DESCRIPTION_NOTIFICATION_TYPES =
+            CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_INFORMATIONAL | 372;
+
+
+
+  /**
+   * The message ID for the message that will be used if an invalid notification
+   * type is specified.  This takes two arguments, which are the DN of the
+   * configuration entry and the invalid notification type.
+   */
+  public static final int MSGID_ERRORLOG_ACCTNOTHANDLER_INVALID_TYPE =
+       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 373;
+
+
+
+  /**
+   * The message ID for the message that will be used if an error occurs while
+   * attempting to determine the account status notification types.  This takes
+   * two arguments, which are the DN of the configuration entry and a string
+   * representation of the exception that occurred.
+   */
+  public static final int
+       MSGID_ERRORLOG_ACCTNOTHANDLER_CANNOT_GET_NOTIFICATION_TYPES =
+            CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 374;
+
+
+
+  /**
+   * The message ID for the message that will be written to the error log
+   * whenever an account status notification is generated.  This takes four
+   * arguments, which are the name of the account status notification type, the
+   * user DN, the message ID, and the message string.
+   */
+  public static final int MSGID_ERRORLOG_ACCTNOTHANDLER_NOTIFICATION =
+       CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_NOTICE | 375;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
    */
@@ -5729,6 +5773,23 @@
                     "value for configuration attribute " +
                     ATTR_PASSWORD_FORMAT + ", which is used to specify the " +
                     "format for the generated passwords:  %s.");
+
+
+    registerMessage(
+         MSGID_ERRORLOG_ACCTNOTHANDLER_DESCRIPTION_NOTIFICATION_TYPES,
+         "Specifies the status notification types for which log messages " +
+         "should be generated.  It is a multivalued attribute, and changes " +
+         "will take effect immediately.");
+    registerMessage(MSGID_ERRORLOG_ACCTNOTHANDLER_INVALID_TYPE,
+                    "Configuration entry %s contains unrecognized account " +
+                    "status notification type %s.");
+    registerMessage(MSGID_ERRORLOG_ACCTNOTHANDLER_CANNOT_GET_NOTIFICATION_TYPES,
+                    "An error occurred while attempting to determine " +
+                    "the account status notification types from " +
+                    "configuration entry %s:  %s.");
+    registerMessage(MSGID_ERRORLOG_ACCTNOTHANDLER_NOTIFICATION,
+                    "Account-Status-Notification type='%s' userdn='%s' " +
+                    "id=%d msg='%s'");
   }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java
index 80b0b2a..246c66b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/UtilityMessages.java
@@ -2004,7 +2004,7 @@
     registerMessage(MSGID_ACCTNOTTYPE_PASSWORD_EXPIRING,
                     "password-expiring");
     registerMessage(MSGID_ACCTNOTTYPE_PASSWORD_RESET,
-                    "password-reset-by-administrator");
+                    "password-reset");
     registerMessage(MSGID_ACCTNOTTYPE_PASSWORD_CHANGED,
                     "password-changed");
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPConnection.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPConnection.java
index 4afb054..712a518 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPConnection.java
@@ -47,7 +47,8 @@
 
 import static org.opends.server.loggers.Debug.*;
 import static org.opends.server.messages.MessageHandler.*;
-import static org.opends.server.messages.CoreMessages.*;
+import static org.opends.server.messages.CoreMessages.
+                   MSGID_RESULT_CLIENT_SIDE_CONNECT_ERROR;
 import static org.opends.server.messages.ToolMessages.*;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotification.java b/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotification.java
index 0c4f5dc..2311153 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotification.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotification.java
@@ -49,6 +49,9 @@
   // The notification type for this account status notification.
   private AccountStatusNotificationType notificationType;
 
+  // The DN of the user entry to which this notification applies.
+  private DN userDN;
+
   // The message ID for the account status notification message.
   private int messageID;
 
@@ -64,19 +67,22 @@
    *
    * @param  notificationType  The type for this account status
    *                           notification.
+   * @param  userDN            The DN of the user entry to which
+   *                           this notification applies.
    * @param  messageID         The unique ID for this notification.
    * @param  message           The human-readable message for this
    *                           notification.
    */
   public AccountStatusNotification(
               AccountStatusNotificationType notificationType,
-              int messageID, String message)
+              DN userDN, int messageID, String message)
   {
     assert debugEnter(CLASS_NAME, String.valueOf(notificationType),
                       String.valueOf(messageID),
                       String.valueOf(message));
 
     this.notificationType = notificationType;
+    this.userDN           = userDN;
     this.messageID        = messageID;
     this.message          = message;
   }
@@ -100,6 +106,22 @@
 
 
   /**
+   * Retrieves the DN of the user entry to which this notification
+   * applies.
+   *
+   * @return  The DN of the user entry to which this notification
+   *          applies.
+   */
+  public DN getUserDN()
+  {
+    assert debugEnter(CLASS_NAME, "getUserDN");
+
+    return userDN;
+  }
+
+
+
+  /**
    * Retrieves the message ID for the account status notification
    * message.
    *
@@ -143,8 +165,8 @@
     assert debugEnter(CLASS_NAME, "toString");
 
     return "AccountStatusNotification(type=" +
-           String.valueOf(notificationType) + ",id=" + messageID +
-           ",message=" + message + ")";
+           notificationType.getNotificationTypeName() + ",dn=" +
+           userDN + ",id=" + messageID + ",message=" + message + ")";
   }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotificationType.java b/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotificationType.java
index ba617ca..b51ed23 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotificationType.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/AccountStatusNotificationType.java
@@ -215,6 +215,89 @@
 
 
   /**
+   * Retrieves the account status notification type with the specified
+   * name.
+   *
+   * @param  name  The name for the account status notification type
+   *               to retrieve.
+   *
+   * @return  The requested account status notification type, or
+   *          <CODE>null</CODE> if there is no type with the given
+   *          name.
+   */
+  public static AccountStatusNotificationType typeForName(String name)
+  {
+    String lowerName = toLowerCase(name);
+
+    if (lowerName.equals(getMessage(
+             MSGID_ACCTNOTTYPE_ACCOUNT_TEMPORARILY_LOCKED)))
+    {
+      return ACCOUNT_TEMPORARILY_LOCKED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_PERMANENTLY_LOCKED)))
+    {
+      return ACCOUNT_PERMANENTLY_LOCKED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_UNLOCKED)))
+    {
+      return ACCOUNT_UNLOCKED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_IDLE_LOCKED)))
+    {
+      return ACCOUNT_IDLE_LOCKED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_RESET_LOCKED)))
+    {
+      return ACCOUNT_RESET_LOCKED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_DISABLED)))
+    {
+      return ACCOUNT_DISABLED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_ENABLED)))
+    {
+      return ACCOUNT_ENABLED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_ACCOUNT_EXPIRED)))
+    {
+      return ACCOUNT_EXPIRED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_PASSWORD_EXPIRED)))
+    {
+      return PASSWORD_EXPIRED;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_PASSWORD_EXPIRING)))
+    {
+      return PASSWORD_EXPIRING;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_PASSWORD_RESET)))
+    {
+      return PASSWORD_RESET;
+    }
+    else if (lowerName.equals(getMessage(
+                  MSGID_ACCTNOTTYPE_PASSWORD_CHANGED)))
+    {
+      return PASSWORD_CHANGED;
+    }
+    else
+    {
+      return null;
+    }
+  }
+
+
+
+  /**
    * Retrieves the notification type identifier for this account
    * status notification type.
    *
@@ -229,6 +312,18 @@
 
 
   /**
+   * Retrieves the name for this account status notification type.
+   *
+   * @return  The name for this account status notification type.
+   */
+  public String getNotificationTypeName()
+  {
+    return getMessage(notificationTypeID);
+  }
+
+
+
+  /**
    * Retrieves a string representation of this account status
    * notification type.
    *

--
Gitblit v1.10.0