/** {@inheritDoc} */
  @Override()
  public boolean passwordIsAcceptable(
      ByteString newPassword,
      Set<ByteString> currentPasswords,
      Operation operation,
      Entry userEntry,
      MessageBuilder invalidReason) {
    // Get a handle to the current configuration and see if we need to count
    // the number of repeated characters in the password.
    RepeatedCharactersPasswordValidatorCfg config = currentConfig;
    int maxRepeats = config.getMaxConsecutiveLength();
    if (maxRepeats <= 0) {
      // We don't need to check anything, so the password will be acceptable.
      return true;
    }

    // Get the password as a string.  If we should use case-insensitive
    // validation, then convert it to use all lowercase characters.
    String passwordString = newPassword.toString();
    if (!config.isCaseSensitiveValidation()) {
      passwordString = passwordString.toLowerCase();
    }

    // Create variables to keep track of the last character we've seen and how
    // many times we have seen it.
    char lastCharacter = '\u0000';
    int consecutiveCount = 0;

    // Iterate through the characters in the password.  If the consecutive
    // count ever gets too high, then fail.
    for (int i = 0; i < passwordString.length(); i++) {
      char currentCharacter = passwordString.charAt(i);
      if (currentCharacter == lastCharacter) {
        consecutiveCount++;
        if (consecutiveCount > maxRepeats) {
          Message message = ERR_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE.get(maxRepeats);
          invalidReason.append(message);
          return false;
        }
      } else {
        lastCharacter = currentCharacter;
        consecutiveCount = 1;
      }
    }

    return true;
  }
  private Object[] generateValues(String password) throws Exception {
    ByteString bytePassword = ByteString.valueOf(password);
    SaltedMD5PasswordStorageScheme scheme = new SaltedMD5PasswordStorageScheme();

    ConfigEntry configEntry =
        DirectoryServer.getConfigEntry(
            DN.decode("cn=Salted MD5,cn=Password Storage Schemes,cn=config"));

    SaltedMD5PasswordStorageSchemeCfg configuration =
        AdminTestCaseUtils.getConfiguration(
            SaltedMD5PasswordStorageSchemeCfgDefn.getInstance(), configEntry.getEntry());

    scheme.initializePasswordStorageScheme(configuration);

    ByteString encodedAuthPassword = scheme.encodePasswordWithScheme(bytePassword);

    return new Object[] {encodedAuthPassword.toString(), password, true};
  }
  /**
   * Perform the LDAP EXTENDED operation and send the result back to the client.
   *
   * @param objFactory The object factory for this operation.
   * @param extendedRequest The extended request for this operation.
   * @param controls Any required controls (e.g. for proxy authz).
   * @return The result of the extended operation.
   * @throws IOException If an I/O problem occurs.
   * @throws LDAPException If an error occurs while interacting with an LDAP element.
   * @throws ASN1Exception If an error occurs while interacting with an ASN.1 element.
   */
  public ExtendedResponse doOperation(
      ObjectFactory objFactory,
      ExtendedRequest extendedRequest,
      List<org.opends.server.types.Control> controls)
      throws IOException, LDAPException, ASN1Exception {
    ExtendedResponse extendedResponse = objFactory.createExtendedResponse();
    extendedResponse.setRequestID(extendedRequest.getRequestID());

    String requestName = extendedRequest.getRequestName();
    Object value = extendedRequest.getRequestValue();
    ByteString asnValue = ByteStringUtility.convertValue(value);

    // Create and send the LDAP request to the server.
    ProtocolOp op = new ExtendedRequestProtocolOp(requestName, asnValue);
    LDAPMessage msg = new LDAPMessage(DSMLServlet.nextMessageID(), op, controls);
    connection.getLDAPWriter().writeMessage(msg);

    // Read and decode the LDAP response from the server.
    LDAPMessage responseMessage = connection.getLDAPReader().readMessage();

    ExtendedResponseProtocolOp extendedOp = responseMessage.getExtendedResponseProtocolOp();
    int resultCode = extendedOp.getResultCode();
    Message errorMessage = extendedOp.getErrorMessage();

    // Set the result code and error message for the DSML response.
    extendedResponse.setResponseName(extendedOp.getOID());

    ByteString rawValue = extendedOp.getValue();
    value = null;
    if (rawValue != null) {
      if (responseIsString(requestName)) {
        value = rawValue.toString();
      } else {
        value = rawValue.toByteArray();
      }
    }
    extendedResponse.setResponse(value);
    extendedResponse.setErrorMessage(errorMessage != null ? errorMessage.toString() : null);
    ResultCode code = ResultCodeFactory.create(objFactory, resultCode);
    extendedResponse.setResultCode(code);

    return extendedResponse;
  }
  /**
   * Decodes the contents of the provided byte sequence as an ldap syntax definition according to
   * the rules of this syntax. Note that the provided byte sequence value does not need to be
   * normalized (and in fact, it should not be in order to allow the desired capitalization to be
   * preserved).
   *
   * @param value The byte sequence containing the value to decode (it does not need to be
   *     normalized).
   * @param schema The schema to use to resolve references to other schema elements.
   * @param allowUnknownElements Indicates whether to allow values that are not defined in the
   *     server schema. This should only be true when called by {@code valueIsAcceptable}. Not used
   *     for LDAP Syntaxes
   * @return The decoded ldapsyntax definition.
   * @throws DirectoryException If the provided value cannot be decoded as an ldapsyntax definition.
   */
  public static LDAPSyntaxDescription decodeLDAPSyntax(
      ByteSequence value, Schema schema, boolean allowUnknownElements) throws DirectoryException {
    // Get string representations of the provided value using the provided form.
    String valueStr = value.toString();

    // We'll do this a character at a time.  First, skip over any leading
    // whitespace.
    int pos = 0;
    int length = valueStr.length();
    while ((pos < length) && (valueStr.charAt(pos) == ' ')) {
      pos++;
    }

    if (pos >= length) {
      // This means that the value was empty or contained only whitespace.  That
      // is illegal.

      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_EMPTY_VALUE.get();
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // The next character must be an open parenthesis.  If it is not, then that
    // is an error.
    char c = valueStr.charAt(pos++);
    if (c != '(') {

      Message message =
          ERR_ATTR_SYNTAX_LDAPSYNTAX_EXPECTED_OPEN_PARENTHESIS.get(
              valueStr, (pos - 1), String.valueOf(c));
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // Skip over any spaces immediately following the opening parenthesis.
    while ((pos < length) && ((c = valueStr.charAt(pos)) == ' ')) {
      pos++;
    }

    if (pos >= length) {
      // This means that the end of the value was reached before we could find
      // the OID.  Ths is illegal.
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    int oidStartPos = pos;
    if (isDigit(c)) {
      // This must be a numeric OID.  In that case, we will accept only digits
      // and periods, but not consecutive periods.
      boolean lastWasPeriod = false;
      while ((pos < length)
          && ((c = valueStr.charAt(pos)) != ' ')
          && (c = valueStr.charAt(pos)) != ')') {
        if (c == '.') {
          if (lastWasPeriod) {
            Message message =
                ERR_ATTR_SYNTAX_LDAPSYNTAX_DOUBLE_PERIOD_IN_NUMERIC_OID.get(valueStr, (pos - 1));
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
          } else {
            lastWasPeriod = true;
          }
        } else if (!isDigit(c)) {
          // This must have been an illegal character.
          Message message =
              ERR_ATTR_SYNTAX_LDAPSYNTAX_ILLEGAL_CHAR_IN_NUMERIC_OID.get(
                  valueStr, String.valueOf(c), (pos - 1));
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        } else {
          lastWasPeriod = false;
        }
        pos++;
      }
    } else {
      // This must be a "fake" OID.  In this case, we will only accept
      // alphabetic characters, numeric digits, and the hyphen.
      while ((pos < length)
          && ((c = valueStr.charAt(pos)) != ' ')
          && (c = valueStr.charAt(pos)) != ')') {
        if (isAlpha(c)
            || isDigit(c)
            || (c == '-')
            || ((c == '_') && DirectoryServer.allowAttributeNameExceptions())) {
          // This is fine.  It is an acceptable character.
          pos++;
        } else {
          // This must have been an illegal character.
          Message message =
              ERR_ATTR_SYNTAX_LDAPSYNTAX_ILLEGAL_CHAR_IN_STRING_OID.get(
                  valueStr, String.valueOf(c), (pos - 1));
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
      }
    }

    // If we're at the end of the value, then it isn't a valid attribute type
    // description.  Otherwise, parse out the OID.
    String oid;
    if (pos >= length) {
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    } else {
      oid = toLowerCase(valueStr.substring(oidStartPos, pos));
    }

    // Skip over the space(s) after the OID.
    while ((pos < length) && ((c = valueStr.charAt(pos)) == ' ')) {
      pos++;
    }

    if (pos >= length) {
      // This means that the end of the value was reached before we could find
      // the OID.  Ths is illegal.
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // At this point, we should have a pretty specific syntax that describes
    // what may come next, but some of the components are optional and it would
    // be pretty easy to put something in the wrong order, so we will be very
    // flexible about what we can accept.  Just look at the next token, figure
    // out what it is and how to treat what comes after it, then repeat until
    // we get to the end of the value.  But before we start, set default values
    // for everything else we might need to know.
    String description = null;
    LDAPSyntaxDescriptionSyntax syntax = null;
    HashMap<String, List<String>> extraProperties = new LinkedHashMap<String, List<String>>();
    boolean hasXSyntaxToken = false;

    while (true) {
      StringBuilder tokenNameBuffer = new StringBuilder();
      pos = readTokenName(valueStr, tokenNameBuffer, pos);
      String tokenName = tokenNameBuffer.toString();
      String lowerTokenName = toLowerCase(tokenName);
      if (tokenName.equals(")")) {
        // We must be at the end of the value.  If not, then that's a problem.
        if (pos < length) {
          Message message =
              ERR_ATTR_SYNTAX_LDAPSYNTAX_UNEXPECTED_CLOSE_PARENTHESIS.get(valueStr, (pos - 1));
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }

        break;
      } else if (lowerTokenName.equals("desc")) {
        // This specifies the description for the attribute type.  It is an
        // arbitrary string of characters enclosed in single quotes.
        StringBuilder descriptionBuffer = new StringBuilder();
        pos = readQuotedString(valueStr, descriptionBuffer, pos);
        description = descriptionBuffer.toString();
      } else if (lowerTokenName.equals("x-subst")) {
        if (hasXSyntaxToken) {
          // We've already seen syntax extension. More than 1 is not allowed
          Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TOO_MANY_EXTENSIONS.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
        hasXSyntaxToken = true;
        StringBuilder woidBuffer = new StringBuilder();
        pos = readQuotedString(valueStr, woidBuffer, pos);
        String syntaxOID = toLowerCase(woidBuffer.toString());
        AttributeSyntax<?> subSyntax = schema.getSyntax(syntaxOID);
        if (subSyntax == null) {
          Message message =
              ERR_ATTR_SYNTAX_LDAPSYNTAX_UNKNOWN_SYNTAX.get(String.valueOf(oid), syntaxOID);
          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
        }
        syntax = new SubstitutionSyntax(subSyntax, valueStr, description, oid);
      } else if (lowerTokenName.equals("x-pattern")) {
        if (hasXSyntaxToken) {
          // We've already seen syntax extension. More than 1 is not allowed
          Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TOO_MANY_EXTENSIONS.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
        hasXSyntaxToken = true;
        StringBuilder regexBuffer = new StringBuilder();
        pos = readQuotedString(valueStr, regexBuffer, pos);
        String regex = regexBuffer.toString().trim();
        if (regex.length() == 0) {
          Message message = WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_NO_PATTERN.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }

        try {
          Pattern pattern = Pattern.compile(regex);
          syntax = new RegexSyntax(pattern, valueStr, description, oid);
        } catch (Exception e) {
          Message message = WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_PATTERN.get(valueStr, regex);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
      } else if (lowerTokenName.equals("x-enum")) {
        if (hasXSyntaxToken) {
          // We've already seen syntax extension. More than 1 is not allowed
          Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TOO_MANY_EXTENSIONS.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
        hasXSyntaxToken = true;
        LinkedList<String> values = new LinkedList<String>();
        pos = readExtraParameterValues(valueStr, values, pos);

        if (values.isEmpty()) {
          Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_ENUM_NO_VALUES.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
        // Parse all enum values, check for uniqueness
        LinkedList<ByteSequence> entries = new LinkedList<ByteSequence>();
        for (String v : values) {
          ByteString entry = ByteString.valueOf(v);
          if (entries.contains(entry)) {
            Message message =
                WARN_ATTR_SYNTAX_LDAPSYNTAX_ENUM_DUPLICATE_VALUE.get(
                    valueStr, entry.toString(), pos);
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
          }
          entries.add(entry);
        }
        syntax = new EnumSyntax(entries, valueStr, description, oid);
      } else if (tokenName.matches("X\\-[_\\p{Alpha}-]+")) {
        // This must be a non-standard property and it must be followed by
        // either a single value in single quotes or an open parenthesis
        // followed by one or more values in single quotes separated by spaces
        // followed by a close parenthesis.
        List<String> valueList = new ArrayList<String>();
        pos = readExtraParameterValues(valueStr, valueList, pos);
        extraProperties.put(tokenName, valueList);
      } else {
        // Unknown Token
        Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_UNKNOWN_EXT.get(valueStr, tokenName, pos);
        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
      }
    }
    if (syntax == null) {
      // Create a plain Syntax. That seems to be required by export/import
      // Schema backend.
      syntax = new LDAPSyntaxDescriptionSyntax();
    }

    CommonSchemaElements.checkSafeProperties(extraProperties);

    // Since we reached here it means everything is OK.
    return new LDAPSyntaxDescription(
        valueStr, syntax,
        description, extraProperties);
  }