/*
* 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 legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
package org.opends.server.protocols.ldap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.FilterType;
import org.opends.server.types.LDAPException;
import org.opends.server.types.RawFilter;
import org.opends.server.types.SearchFilter;
import static org.opends.messages.ProtocolMessages.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines the data structures and methods to use when interacting
* with an LDAP search filter, which defines a set of criteria for locating
* entries in a search request.
*/
public class LDAPFilter
extends RawFilter
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
private static LDAPFilter objectClassPresent;
/** The set of subAny elements for substring filters. */
private ArrayList<ByteString> subAnyElements;
/** The set of filter components for AND and OR filters. */
private ArrayList<RawFilter> filterComponents;
/** Indicates whether to match on DN attributes for extensible match filters. */
private boolean dnAttributes;
/** The assertion value for several filter types. */
private ByteString assertionValue;
/** The subFinal element for substring filters. */
private ByteString subFinalElement;
/** The subInitial element for substring filters. */
private ByteString subInitialElement;
/** The filter type for this filter. */
private FilterType filterType;
/** The filter component for NOT filters. */
private RawFilter notComponent;
/** The attribute type for several filter types. */
private String attributeType;
/** The matching rule ID for extensible matching filters. */
private String matchingRuleID;
/**
* Creates a new LDAP filter with the provided information. Note that this
* constructor is only intended for use by the {@code RawFilter} class and any
* use of this constructor outside of that class must be very careful to
* ensure that all of the appropriate element types have been provided for the
* associated filter type.
*
* @param filterType The filter type for this filter.
* @param filterComponents The filter components for AND and OR filters.
* @param notComponent The filter component for NOT filters.
* @param attributeType The attribute type for this filter.
* @param assertionValue The assertion value for this filter.
* @param subInitialElement The subInitial element for substring filters.
* @param subAnyElements The subAny elements for substring filters.
* @param subFinalElement The subFinal element for substring filters.
* @param matchingRuleID The matching rule ID for extensible filters.
* @param dnAttributes The dnAttributes flag for extensible filters.
*/
public LDAPFilter(FilterType filterType,
ArrayList<RawFilter> filterComponents,
RawFilter notComponent, String attributeType,
ByteString assertionValue, ByteString subInitialElement,
ArrayList<ByteString> subAnyElements,
ByteString subFinalElement, String matchingRuleID,
boolean dnAttributes)
{
this.filterType = filterType;
this.filterComponents = filterComponents;
this.notComponent = notComponent;
this.attributeType = attributeType;
this.assertionValue = assertionValue;
this.subInitialElement = subInitialElement;
this.subAnyElements = subAnyElements;
this.subFinalElement = subFinalElement;
this.matchingRuleID = matchingRuleID;
this.dnAttributes = dnAttributes;
}
/**
* Creates a new LDAP filter from the provided search filter.
*
* @param filter The search filter to use to create this LDAP filter.
*/
public LDAPFilter(SearchFilter filter)
{
this.filterType = filter.getFilterType();
switch (filterType)
{
case AND:
case OR:
Collection<SearchFilter> comps = filter.getFilterComponents();
filterComponents = new ArrayList<>(comps.size());
for (SearchFilter f : comps)
{
filterComponents.add(new LDAPFilter(f));
}
notComponent = null;
attributeType = null;
assertionValue = null;
subInitialElement = null;
subAnyElements = null;
subFinalElement = null;
matchingRuleID = null;
dnAttributes = false;
break;
case NOT:
notComponent = new LDAPFilter(filter.getNotComponent());
filterComponents = null;
attributeType = null;
assertionValue = null;
subInitialElement = null;
subAnyElements = null;
subFinalElement = null;
matchingRuleID = null;
dnAttributes = false;
break;
case EQUALITY:
case GREATER_OR_EQUAL:
case LESS_OR_EQUAL:
case APPROXIMATE_MATCH:
attributeType = filter.getAttributeType().getNameOrOID();
assertionValue = filter.getAssertionValue();
filterComponents = null;
notComponent = null;
subInitialElement = null;
subAnyElements = null;
subFinalElement = null;
matchingRuleID = null;
dnAttributes = false;
break;
case SUBSTRING:
attributeType = filter.getAttributeType().getNameOrOID();
ByteString bs = filter.getSubInitialElement();
if (bs == null)
{
subInitialElement = null;
}
else
{
subInitialElement = bs;
}
bs = filter.getSubFinalElement();
if (bs == null)
{
subFinalElement = null;
}
else
{
subFinalElement = bs;
}
List<ByteString> subAnyStrings = filter.getSubAnyElements();
if (subAnyStrings == null)
{
subAnyElements = null;
}
else
{
subAnyElements = new ArrayList<>(subAnyStrings);
}
filterComponents = null;
notComponent = null;
assertionValue = null;
matchingRuleID = null;
dnAttributes = false;
break;
case PRESENT:
attributeType = filter.getAttributeType().getNameOrOID();
filterComponents = null;
notComponent = null;
assertionValue = null;
subInitialElement = null;
subAnyElements = null;
subFinalElement = null;
matchingRuleID = null;
dnAttributes = false;
break;
case EXTENSIBLE_MATCH:
dnAttributes = filter.getDNAttributes();
matchingRuleID = filter.getMatchingRuleID();
AttributeType attrType = filter.getAttributeType();
if (attrType == null)
{
attributeType = null;
}
else
{
attributeType = attrType.getNameOrOID();
}
assertionValue = filter.getAssertionValue();
filterComponents = null;
notComponent = null;
subInitialElement = null;
subAnyElements = null;
subFinalElement = null;
break;
}
}
/**
* Decodes the provided string into an LDAP search filter.
*
* @param filterString The string representation of the search filter to
* decode.
*
* @return The decoded LDAP search filter.
*
* @throws LDAPException If the provided string does not represent a valid
* LDAP search filter.
*/
public static LDAPFilter decode(String filterString)
throws LDAPException
{
if (filterString == null)
{
LocalizableMessage message = ERR_LDAP_FILTER_STRING_NULL.get();
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
try
{
return decode(filterString, 0, filterString.length());
}
catch (LDAPException le)
{
logger.traceException(le);
throw le;
}
catch (Exception e)
{
logger.traceException(e);
LocalizableMessage message = ERR_LDAP_FILTER_UNCAUGHT_EXCEPTION.get(filterString, e);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
}
}
/**
* Decodes the provided string into an LDAP search filter.
*
* @param filterString The string representation of the search filter to
* decode.
* @param startPos The position of the first character in the filter
* to parse.
* @param endPos The position of the first character after the end of
* the filter to parse.
*
* @return The decoded LDAP search filter.
*
* @throws LDAPException If the provided string does not represent a valid
* LDAP search filter.
*/
private static LDAPFilter decode(String filterString, int startPos,
int endPos)
throws LDAPException
{
// Make sure that the length is sufficient for a valid search filter.
int length = endPos - startPos;
if (length <= 0)
{
LocalizableMessage message = ERR_LDAP_FILTER_STRING_NULL.get();
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
// If the filter is enclosed in a pair of apostrophes ("single-quotes") it
// is invalid (issue #1024).
if (1 < filterString.length()
&& filterString.startsWith("'") && filterString.endsWith("'"))
{
LocalizableMessage message =
ERR_LDAP_FILTER_ENCLOSED_IN_APOSTROPHES.get(filterString);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
// If the filter is surrounded by parentheses (which it should be), then
// strip them off.
if (filterString.charAt(startPos) == '(')
{
if (filterString.charAt(endPos-1) == ')')
{
startPos++;
endPos--;
}
else
{
LocalizableMessage message = ERR_LDAP_FILTER_MISMATCHED_PARENTHESES.get(
filterString, startPos, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
}
// Look at the first character. If it is a '&' then it is an AND search.
// If it is a '|' then it is an OR search. If it is a '!' then it is a NOT
// search.
char c = filterString.charAt(startPos);
if (c == '&')
{
return decodeCompoundFilter(FilterType.AND, filterString, startPos+1,
endPos);
}
else if (c == '|')
{
return decodeCompoundFilter(FilterType.OR, filterString, startPos+1,
endPos);
}
else if (c == '!')
{
return decodeCompoundFilter(FilterType.NOT, filterString, startPos+1,
endPos);
}
// If we've gotten here, then it must be a simple filter. It must have an
// equal sign at some point, so find it.
int equalPos = -1;
for (int i=startPos; i < endPos; i++)
{
if (filterString.charAt(i) == '=')
{
equalPos = i;
break;
}
}
if (equalPos <= startPos)
{
LocalizableMessage message =
ERR_LDAP_FILTER_NO_EQUAL_SIGN.get(filterString, startPos, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
// Look at the character immediately before the equal sign, because it may
// help determine the filter type.
int attrEndPos;
FilterType filterType;
switch (filterString.charAt(equalPos-1))
{
case '~':
filterType = FilterType.APPROXIMATE_MATCH;
attrEndPos = equalPos-1;
break;
case '>':
filterType = FilterType.GREATER_OR_EQUAL;
attrEndPos = equalPos-1;
break;
case '<':
filterType = FilterType.LESS_OR_EQUAL;
attrEndPos = equalPos-1;
break;
case ':':
return decodeExtensibleMatchFilter(filterString, startPos, equalPos,
endPos);
default:
filterType = FilterType.EQUALITY;
attrEndPos = equalPos;
break;
}
// The part of the filter string before the equal sign should be the
// attribute type. Make sure that the characters it contains are acceptable
// for attribute types, including those allowed by attribute name
// exceptions (ASCII letters and digits, the dash, and the underscore). We
// also need to allow attribute options, which includes the semicolon and
// the equal sign.
String attrType = filterString.substring(startPos, attrEndPos);
for (int i=0; i < attrType.length(); i++)
{
switch (attrType.charAt(i))
{
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case ';':
case '=':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case '_':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
// These are all OK.
break;
case '.':
case '/':
case ':':
case '<':
case '>':
case '?':
case '@':
case '[':
case '\\':
case ']':
case '^':
case '`':
// These are not allowed, but they are explicitly called out because
// they are included in the range of values between '-' and 'z', and
// making sure all possible characters are included can help make the
// switch statement more efficient. We'll fall through to the default
// clause to reject them.
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_CHAR_IN_ATTR_TYPE.get(
attrType, attrType.charAt(i), i);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
}
// Get the attribute value.
String valueStr = filterString.substring(equalPos+1, endPos);
if (valueStr.length() == 0)
{
return new LDAPFilter(filterType, null, null, attrType,
ByteString.empty(), null, null, null, null,
false);
}
else if (valueStr.equals("*"))
{
return new LDAPFilter(FilterType.PRESENT, null, null, attrType, null,
null, null, null, null, false);
}
else if (valueStr.indexOf('*') >= 0)
{
return decodeSubstringFilter(filterString, attrType, equalPos, endPos);
}
else
{
boolean hasEscape = false;
byte[] valueBytes = getBytes(valueStr);
for (byte valueByte : valueBytes)
{
if (valueByte == 0x5C) // The backslash character
{
hasEscape = true;
break;
}
}
ByteString value;
if (hasEscape)
{
ByteStringBuilder valueBuffer =
new ByteStringBuilder(valueStr.length());
for (int i=0; i < valueBytes.length; i++)
{
if (valueBytes[i] == 0x5C) // The backslash character
{
// The next two bytes must be the hex characters that comprise the
// binary value.
if (i + 2 >= valueBytes.length)
{
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
byte byteValue = 0;
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue = (byte) 0x10;
break;
case 0x32: // '2'
byteValue = (byte) 0x20;
break;
case 0x33: // '3'
byteValue = (byte) 0x30;
break;
case 0x34: // '4'
byteValue = (byte) 0x40;
break;
case 0x35: // '5'
byteValue = (byte) 0x50;
break;
case 0x36: // '6'
byteValue = (byte) 0x60;
break;
case 0x37: // '7'
byteValue = (byte) 0x70;
break;
case 0x38: // '8'
byteValue = (byte) 0x80;
break;
case 0x39: // '9'
byteValue = (byte) 0x90;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue = (byte) 0xA0;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue = (byte) 0xB0;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue = (byte) 0xC0;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue = (byte) 0xD0;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue = (byte) 0xE0;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue = (byte) 0xF0;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue |= (byte) 0x01;
break;
case 0x32: // '2'
byteValue |= (byte) 0x02;
break;
case 0x33: // '3'
byteValue |= (byte) 0x03;
break;
case 0x34: // '4'
byteValue |= (byte) 0x04;
break;
case 0x35: // '5'
byteValue |= (byte) 0x05;
break;
case 0x36: // '6'
byteValue |= (byte) 0x06;
break;
case 0x37: // '7'
byteValue |= (byte) 0x07;
break;
case 0x38: // '8'
byteValue |= (byte) 0x08;
break;
case 0x39: // '9'
byteValue |= (byte) 0x09;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue |= (byte) 0x0A;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue |= (byte) 0x0B;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue |= (byte) 0x0C;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue |= (byte) 0x0D;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue |= (byte) 0x0E;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue |= (byte) 0x0F;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
valueBuffer.appendByte(byteValue);
}
else
{
valueBuffer.appendByte(valueBytes[i]);
}
}
value = valueBuffer.toByteString();
}
else
{
value = ByteString.wrap(valueBytes);
}
return new LDAPFilter(filterType, null, null, attrType, value, null, null,
null, null, false);
}
}
/**
* Decodes a set of filters from the provided filter string within the
* indicated range.
*
* @param filterType The filter type for this compound filter. It must be
* an AND, OR or NOT filter.
* @param filterString The string containing the filter information to
* decode.
* @param startPos The position of the first character in the set of
* filters to decode.
* @param endPos The position of the first character after the end of
* the set of filters to decode.
*
* @return The decoded LDAP filter.
*
* @throws LDAPException If a problem occurs while attempting to decode the
* compound filter.
*/
private static LDAPFilter decodeCompoundFilter(FilterType filterType,
String filterString,
int startPos, int endPos)
throws LDAPException
{
// Create a list to hold the returned components.
ArrayList<RawFilter> filterComponents = new ArrayList<>();
// If the end pos is equal to the start pos, then there are no components.
if (startPos == endPos)
{
if (filterType == FilterType.NOT)
{
LocalizableMessage message =
ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
else
{
// This is valid and will be treated as a TRUE/FALSE filter.
return new LDAPFilter(filterType, filterComponents, null, null, null,
null, null, null, null, false);
}
}
// The first and last characters must be parentheses. If not, then that's
// an error.
if (filterString.charAt(startPos) != '(' ||
filterString.charAt(endPos-1) != ')')
{
LocalizableMessage message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get(
filterString, startPos, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
// Iterate through the characters in the value. Whenever an open
// parenthesis is found, locate the corresponding close parenthesis by
// counting the number of intermediate open/close parentheses.
int pendingOpens = 0;
int openPos = -1;
for (int i=startPos; i < endPos; i++)
{
char c = filterString.charAt(i);
if (c == '(')
{
if (openPos < 0)
{
openPos = i;
}
pendingOpens++;
}
else if (c == ')')
{
pendingOpens--;
if (pendingOpens == 0)
{
filterComponents.add(decode(filterString, openPos, i+1));
openPos = -1;
}
else if (pendingOpens < 0)
{
LocalizableMessage message = ERR_LDAP_FILTER_NO_CORRESPONDING_OPEN_PARENTHESIS.
get(filterString, i);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
}
else if (pendingOpens <= 0)
{
LocalizableMessage message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get(
filterString, startPos, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
}
// At this point, we have parsed the entire set of filter components. The
// list of open parenthesis positions must be empty.
if (pendingOpens != 0)
{
LocalizableMessage message = ERR_LDAP_FILTER_NO_CORRESPONDING_CLOSE_PARENTHESIS.get(
filterString, openPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
// We should have everything we need, so return the list.
if (filterType == FilterType.NOT)
{
if (filterComponents.size() != 1)
{
LocalizableMessage message =
ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
RawFilter notComponent = filterComponents.get(0);
return new LDAPFilter(filterType, null, notComponent, null, null,
null, null, null, null, false);
}
else
{
return new LDAPFilter(filterType, filterComponents, null, null, null,
null, null, null, null, false);
}
}
/**
* Decodes a substring search filter component based on the provided
* information.
*
* @param filterString The filter string containing the information to
* decode.
* @param attrType The attribute type for this substring filter
* component.
* @param equalPos The location of the equal sign separating the
* attribute type from the value.
* @param endPos The position of the first character after the end of
* the substring value.
*
* @return The decoded LDAP filter.
*
* @throws LDAPException If a problem occurs while attempting to decode the
* substring filter.
*/
private static LDAPFilter decodeSubstringFilter(String filterString,
String attrType, int equalPos,
int endPos)
throws LDAPException
{
// Get a binary representation of the value.
byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos));
// Find the locations of all the asterisks in the value. Also, check to
// see if there are any escaped values, since they will need special
// treatment.
boolean hasEscape = false;
LinkedList<Integer> asteriskPositions = new LinkedList<>();
for (int i=0; i < valueBytes.length; i++)
{
if (valueBytes[i] == 0x2A) // The asterisk.
{
asteriskPositions.add(i);
}
else if (valueBytes[i] == 0x5C) // The backslash.
{
hasEscape = true;
}
}
// If there were no asterisks, then this isn't a substring filter.
if (asteriskPositions.isEmpty())
{
LocalizableMessage message = ERR_LDAP_FILTER_SUBSTRING_NO_ASTERISKS.get(
filterString, equalPos+1, endPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
// If the value starts with an asterisk, then there is no subInitial
// component. Otherwise, parse out the subInitial.
ByteString subInitial;
int firstPos = asteriskPositions.removeFirst();
if (firstPos == 0)
{
subInitial = null;
}
else
{
if (hasEscape)
{
ByteStringBuilder buffer = new ByteStringBuilder(firstPos);
for (int i=0; i < firstPos; i++)
{
if (valueBytes[i] == 0x5C)
{
// The next two bytes must be the hex characters that comprise the
// binary value.
if (i + 2 >= valueBytes.length)
{
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
byte byteValue = 0;
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue = (byte) 0x10;
break;
case 0x32: // '2'
byteValue = (byte) 0x20;
break;
case 0x33: // '3'
byteValue = (byte) 0x30;
break;
case 0x34: // '4'
byteValue = (byte) 0x40;
break;
case 0x35: // '5'
byteValue = (byte) 0x50;
break;
case 0x36: // '6'
byteValue = (byte) 0x60;
break;
case 0x37: // '7'
byteValue = (byte) 0x70;
break;
case 0x38: // '8'
byteValue = (byte) 0x80;
break;
case 0x39: // '9'
byteValue = (byte) 0x90;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue = (byte) 0xA0;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue = (byte) 0xB0;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue = (byte) 0xC0;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue = (byte) 0xD0;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue = (byte) 0xE0;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue = (byte) 0xF0;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue |= (byte) 0x01;
break;
case 0x32: // '2'
byteValue |= (byte) 0x02;
break;
case 0x33: // '3'
byteValue |= (byte) 0x03;
break;
case 0x34: // '4'
byteValue |= (byte) 0x04;
break;
case 0x35: // '5'
byteValue |= (byte) 0x05;
break;
case 0x36: // '6'
byteValue |= (byte) 0x06;
break;
case 0x37: // '7'
byteValue |= (byte) 0x07;
break;
case 0x38: // '8'
byteValue |= (byte) 0x08;
break;
case 0x39: // '9'
byteValue |= (byte) 0x09;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue |= (byte) 0x0A;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue |= (byte) 0x0B;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue |= (byte) 0x0C;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue |= (byte) 0x0D;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue |= (byte) 0x0E;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue |= (byte) 0x0F;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
buffer.appendByte(byteValue);
}
else
{
buffer.appendByte(valueBytes[i]);
}
}
subInitial = buffer.toByteString();
}
else
{
subInitial = ByteString.wrap(valueBytes, 0, firstPos);
}
}
// Next, process through the rest of the asterisks to get the subAny values.
ArrayList<ByteString> subAny = new ArrayList<>();
for (int asteriskPos : asteriskPositions)
{
int length = asteriskPos - firstPos - 1;
if (hasEscape)
{
ByteStringBuilder buffer = new ByteStringBuilder(length);
for (int i=firstPos+1; i < asteriskPos; i++)
{
if (valueBytes[i] == 0x5C)
{
// The next two bytes must be the hex characters that comprise the
// binary value.
if (i + 2 >= valueBytes.length)
{
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
byte byteValue = 0;
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue = (byte) 0x10;
break;
case 0x32: // '2'
byteValue = (byte) 0x20;
break;
case 0x33: // '3'
byteValue = (byte) 0x30;
break;
case 0x34: // '4'
byteValue = (byte) 0x40;
break;
case 0x35: // '5'
byteValue = (byte) 0x50;
break;
case 0x36: // '6'
byteValue = (byte) 0x60;
break;
case 0x37: // '7'
byteValue = (byte) 0x70;
break;
case 0x38: // '8'
byteValue = (byte) 0x80;
break;
case 0x39: // '9'
byteValue = (byte) 0x90;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue = (byte) 0xA0;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue = (byte) 0xB0;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue = (byte) 0xC0;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue = (byte) 0xD0;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue = (byte) 0xE0;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue = (byte) 0xF0;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue |= (byte) 0x01;
break;
case 0x32: // '2'
byteValue |= (byte) 0x02;
break;
case 0x33: // '3'
byteValue |= (byte) 0x03;
break;
case 0x34: // '4'
byteValue |= (byte) 0x04;
break;
case 0x35: // '5'
byteValue |= (byte) 0x05;
break;
case 0x36: // '6'
byteValue |= (byte) 0x06;
break;
case 0x37: // '7'
byteValue |= (byte) 0x07;
break;
case 0x38: // '8'
byteValue |= (byte) 0x08;
break;
case 0x39: // '9'
byteValue |= (byte) 0x09;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue |= (byte) 0x0A;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue |= (byte) 0x0B;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue |= (byte) 0x0C;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue |= (byte) 0x0D;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue |= (byte) 0x0E;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue |= (byte) 0x0F;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
buffer.appendByte(byteValue);
}
else
{
buffer.appendByte(valueBytes[i]);
}
}
subAny.add(buffer.toByteString());
buffer.clear();
}
else
{
subAny.add(ByteString.wrap(valueBytes, firstPos+1, length));
}
firstPos = asteriskPos;
}
// Finally, see if there is anything after the last asterisk, which would be
// the subFinal value.
ByteString subFinal;
if (firstPos == (valueBytes.length-1))
{
subFinal = null;
}
else
{
int length = valueBytes.length - firstPos - 1;
if (hasEscape)
{
ByteStringBuilder buffer = new ByteStringBuilder(length);
for (int i=firstPos+1; i < valueBytes.length; i++)
{
if (valueBytes[i] == 0x5C)
{
// The next two bytes must be the hex characters that comprise the
// binary value.
if (i + 2 >= valueBytes.length)
{
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
byte byteValue = 0;
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue = (byte) 0x10;
break;
case 0x32: // '2'
byteValue = (byte) 0x20;
break;
case 0x33: // '3'
byteValue = (byte) 0x30;
break;
case 0x34: // '4'
byteValue = (byte) 0x40;
break;
case 0x35: // '5'
byteValue = (byte) 0x50;
break;
case 0x36: // '6'
byteValue = (byte) 0x60;
break;
case 0x37: // '7'
byteValue = (byte) 0x70;
break;
case 0x38: // '8'
byteValue = (byte) 0x80;
break;
case 0x39: // '9'
byteValue = (byte) 0x90;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue = (byte) 0xA0;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue = (byte) 0xB0;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue = (byte) 0xC0;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue = (byte) 0xD0;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue = (byte) 0xE0;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue = (byte) 0xF0;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue |= (byte) 0x01;
break;
case 0x32: // '2'
byteValue |= (byte) 0x02;
break;
case 0x33: // '3'
byteValue |= (byte) 0x03;
break;
case 0x34: // '4'
byteValue |= (byte) 0x04;
break;
case 0x35: // '5'
byteValue |= (byte) 0x05;
break;
case 0x36: // '6'
byteValue |= (byte) 0x06;
break;
case 0x37: // '7'
byteValue |= (byte) 0x07;
break;
case 0x38: // '8'
byteValue |= (byte) 0x08;
break;
case 0x39: // '9'
byteValue |= (byte) 0x09;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue |= (byte) 0x0A;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue |= (byte) 0x0B;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue |= (byte) 0x0C;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue |= (byte) 0x0D;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue |= (byte) 0x0E;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue |= (byte) 0x0F;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
buffer.appendByte(byteValue);
}
else
{
buffer.appendByte(valueBytes[i]);
}
}
subFinal = buffer.toByteString();
}
else
{
subFinal = ByteString.wrap(valueBytes, firstPos+1, length);
}
}
return new LDAPFilter(FilterType.SUBSTRING, null, null, attrType, null,
subInitial, subAny, subFinal, null, false);
}
/**
* Decodes an extensible match filter component based on the provided
* information.
*
* @param filterString The filter string containing the information to
* decode.
* @param startPos The position in the filter string of the first
* character in the extensible match filter.
* @param equalPos The position of the equal sign in the extensible
* match filter.
* @param endPos The position of the first character after the end of
* the extensible match filter.
*
* @return The decoded LDAP filter.
*
* @throws LDAPException If a problem occurs while attempting to decode the
* extensible match filter.
*/
private static LDAPFilter decodeExtensibleMatchFilter(String filterString,
int startPos,
int equalPos,
int endPos)
throws LDAPException
{
String attributeType = null;
boolean dnAttributes = false;
String matchingRuleID = null;
// Look at the first character. If it is a colon, then it must be followed
// by either the string "dn" or the matching rule ID. If it is not, then
// must be the attribute type.
String lowerLeftStr =
toLowerCase(filterString.substring(startPos, equalPos));
if (filterString.charAt(startPos) == ':')
{
// See if it starts with ":dn". Otherwise, it much be the matching rule
// ID.
if (lowerLeftStr.startsWith(":dn:"))
{
dnAttributes = true;
if(startPos+4 < equalPos-1)
{
matchingRuleID = filterString.substring(startPos+4, equalPos-1);
}
}
else
{
matchingRuleID = filterString.substring(startPos+1, equalPos-1);
}
}
else
{
int colonPos = filterString.indexOf(':',startPos);
if (colonPos < 0)
{
LocalizableMessage message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_COLON.get(
filterString, startPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
attributeType = filterString.substring(startPos, colonPos);
// If there is anything left, then it should be ":dn" and/or ":" followed
// by the matching rule ID.
if (colonPos < equalPos-1)
{
if (lowerLeftStr.startsWith(":dn:", colonPos - startPos))
{
dnAttributes = true;
if (colonPos+4 < equalPos-1)
{
matchingRuleID = filterString.substring(colonPos+4, equalPos-1);
}
}
else
{
matchingRuleID = filterString.substring(colonPos+1, equalPos-1);
}
}
}
// Parse out the attribute value.
byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos));
boolean hasEscape = false;
for (byte valueByte : valueBytes)
{
if (valueByte == 0x5C)
{
hasEscape = true;
break;
}
}
ByteString value;
if (hasEscape)
{
ByteStringBuilder valueBuffer = new ByteStringBuilder(valueBytes.length);
for (int i=0; i < valueBytes.length; i++)
{
if (valueBytes[i] == 0x5C) // The backslash character
{
// The next two bytes must be the hex characters that comprise the
// binary value.
if (i + 2 >= valueBytes.length)
{
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
byte byteValue = 0;
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue = (byte) 0x10;
break;
case 0x32: // '2'
byteValue = (byte) 0x20;
break;
case 0x33: // '3'
byteValue = (byte) 0x30;
break;
case 0x34: // '4'
byteValue = (byte) 0x40;
break;
case 0x35: // '5'
byteValue = (byte) 0x50;
break;
case 0x36: // '6'
byteValue = (byte) 0x60;
break;
case 0x37: // '7'
byteValue = (byte) 0x70;
break;
case 0x38: // '8'
byteValue = (byte) 0x80;
break;
case 0x39: // '9'
byteValue = (byte) 0x90;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue = (byte) 0xA0;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue = (byte) 0xB0;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue = (byte) 0xC0;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue = (byte) 0xD0;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue = (byte) 0xE0;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue = (byte) 0xF0;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
switch (valueBytes[++i])
{
case 0x30: // '0'
break;
case 0x31: // '1'
byteValue |= (byte) 0x01;
break;
case 0x32: // '2'
byteValue |= (byte) 0x02;
break;
case 0x33: // '3'
byteValue |= (byte) 0x03;
break;
case 0x34: // '4'
byteValue |= (byte) 0x04;
break;
case 0x35: // '5'
byteValue |= (byte) 0x05;
break;
case 0x36: // '6'
byteValue |= (byte) 0x06;
break;
case 0x37: // '7'
byteValue |= (byte) 0x07;
break;
case 0x38: // '8'
byteValue |= (byte) 0x08;
break;
case 0x39: // '9'
byteValue |= (byte) 0x09;
break;
case 0x41: // 'A'
case 0x61: // 'a'
byteValue |= (byte) 0x0A;
break;
case 0x42: // 'B'
case 0x62: // 'b'
byteValue |= (byte) 0x0B;
break;
case 0x43: // 'C'
case 0x63: // 'c'
byteValue |= (byte) 0x0C;
break;
case 0x44: // 'D'
case 0x64: // 'd'
byteValue |= (byte) 0x0D;
break;
case 0x45: // 'E'
case 0x65: // 'e'
byteValue |= (byte) 0x0E;
break;
case 0x46: // 'F'
case 0x66: // 'f'
byteValue |= (byte) 0x0F;
break;
default:
LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
filterString, equalPos+i+1);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
valueBuffer.appendByte(byteValue);
}
else
{
valueBuffer.appendByte(valueBytes[i]);
}
}
value = valueBuffer.toByteString();
}
else
{
value = ByteString.wrap(valueBytes);
}
// Make sure that the filter has at least one of an attribute description
// and/or a matching rule ID.
if (attributeType == null && matchingRuleID == null)
{
LocalizableMessage message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR.get(
filterString, startPos);
throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
}
return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
attributeType, value, null, null, null,
matchingRuleID, dnAttributes);
}
/**
* Retrieves the filter type for this search filter.
*
* @return The filter type for this search filter.
*/
@Override
public FilterType getFilterType()
{
return filterType;
}
/**
* Retrieves the set of subordinate filter components for AND or OR searches.
* The contents of the returned list may be altered by the caller.
*
* @return The set of subordinate filter components for AND and OR searches,
* or <CODE>null</CODE> if this is not an AND or OR search.
*/
@Override
public ArrayList<RawFilter> getFilterComponents()
{
return filterComponents;
}
/**
* Retrieves the subordinate filter component for NOT searches.
*
* @return The subordinate filter component for NOT searches, or
* <CODE>null</CODE> if this is not a NOT search.
*/
@Override
public RawFilter getNOTComponent()
{
return notComponent;
}
/**
* Retrieves the attribute type for this search filter. This will not be
* applicable for AND, OR, or NOT filters.
*
* @return The attribute type for this search filter, or <CODE>null</CODE> if
* there is none.
*/
@Override
public String getAttributeType()
{
return attributeType;
}
/**
* Retrieves the assertion value for this search filter. This will only be
* applicable for equality, greater or equal, less or equal, approximate, or
* extensible matching filters.
*
* @return The assertion value for this search filter, or <CODE>null</CODE>
* if there is none.
*/
@Override
public ByteString getAssertionValue()
{
return assertionValue;
}
/**
* Retrieves the subInitial component for this substring filter. This is only
* applicable for substring search filters, but even substring filters might
* not have a value for this component.
*
* @return The subInitial component for this substring filter, or
* <CODE>null</CODE> if there is none.
*/
@Override
public ByteString getSubInitialElement()
{
return subInitialElement;
}
/**
* Specifies the subInitial element for this substring filter. This will be
* ignored for all other types of filters.
*
* @param subInitialElement The subInitial element for this substring
* filter.
*/
public void setSubInitialElement(ByteString subInitialElement)
{
this.subInitialElement = subInitialElement;
}
/**
* Retrieves the set of subAny elements for this substring filter. This is
* only applicable for substring search filters, and even then may be null or
* empty for some substring filters.
*
* @return The set of subAny elements for this substring filter, or
* <CODE>null</CODE> if there are none.
*/
@Override
public ArrayList<ByteString> getSubAnyElements()
{
return subAnyElements;
}
/**
* Retrieves the subFinal element for this substring filter. This is not
* applicable for any other filter type, and may not be provided even for some
* substring filters.
*
* @return The subFinal element for this substring filter, or
* <CODE>null</CODE> if there is none.
*/
@Override
public ByteString getSubFinalElement()
{
return subFinalElement;
}
/**
* Retrieves the matching rule ID for this extensible match filter. This is
* not applicable for any other type of filter and may not be included in
* some extensible matching filters.
*
* @return The matching rule ID for this extensible match filter, or
* <CODE>null</CODE> if there is none.
*/
@Override
public String getMatchingRuleID()
{
return matchingRuleID;
}
/**
* Retrieves the value of the DN attributes flag for this extensible match
* filter, which indicates whether to perform matching on the components of
* the DN. This does not apply for any other type of filter.
*
* @return The value of the DN attributes flag for this extensibleMatch
* filter.
*/
@Override
public boolean getDNAttributes()
{
return dnAttributes;
}
/**
* Converts this LDAP filter to a search filter that may be used by the
* Directory Server's core processing.
*
* @return The generated search filter.
*
* @throws DirectoryException If a problem occurs while attempting to
* construct the search filter.
*/
@Override
public SearchFilter toSearchFilter()
throws DirectoryException
{
ArrayList<SearchFilter> subComps;
if (filterComponents == null)
{
subComps = null;
}
else
{
subComps = new ArrayList<>(filterComponents.size());
for (RawFilter f : filterComponents)
{
subComps.add(f.toSearchFilter());
}
}
SearchFilter notComp;
if (notComponent == null)
{
notComp = null;
}
else
{
notComp = notComponent.toSearchFilter();
}
AttributeType attrType;
HashSet<String> options;
if (attributeType == null)
{
attrType = null;
options = null;
}
else
{
int semicolonPos = attributeType.indexOf(';');
if (semicolonPos > 0)
{
String baseName = attributeType.substring(0, semicolonPos);
attrType = DirectoryServer.getAttributeTypeOrDefault(toLowerCase(baseName), baseName);
options = new HashSet<>();
StringTokenizer tokenizer =
new StringTokenizer(attributeType.substring(semicolonPos+1), ";");
while (tokenizer.hasMoreTokens())
{
options.add(tokenizer.nextToken());
}
}
else
{
options = null;
attrType = DirectoryServer.getAttributeTypeOrDefault(toLowerCase(attributeType), attributeType);
}
}
if (assertionValue != null && attrType == null)
{
if (matchingRuleID == null)
{
throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
ERR_LDAP_FILTER_VALUE_WITH_NO_ATTR_OR_MR.get());
}
MatchingRule mr = DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID));
if (mr == null)
{
throw new DirectoryException(ResultCode.INAPPROPRIATE_MATCHING,
ERR_LDAP_FILTER_UNKNOWN_MATCHING_RULE.get(matchingRuleID));
}
}
ArrayList<ByteString> subAnyComps =
subAnyElements != null ? new ArrayList<ByteString>(subAnyElements) : null;
return new SearchFilter(filterType, subComps, notComp, attrType,
options, assertionValue, subInitialElement, subAnyComps,
subFinalElement, matchingRuleID, dnAttributes);
}
/**
* Appends a string representation of this search filter to the provided
* buffer.
*
* @param buffer The buffer to which the information should be appended.
*/
@Override
public void toString(StringBuilder buffer)
{
switch (filterType)
{
case AND:
buffer.append("(&");
for (RawFilter f : filterComponents)
{
f.toString(buffer);
}
buffer.append(")");
break;
case OR:
buffer.append("(|");
for (RawFilter f : filterComponents)
{
f.toString(buffer);
}
buffer.append(")");
break;
case NOT:
buffer.append("(!");
notComponent.toString(buffer);
buffer.append(")");
break;
case EQUALITY:
buffer.append("(");
buffer.append(attributeType);
buffer.append("=");
valueToFilterString(buffer, assertionValue);
buffer.append(")");
break;
case SUBSTRING:
buffer.append("(");
buffer.append(attributeType);
buffer.append("=");
if (subInitialElement != null)
{
valueToFilterString(buffer, subInitialElement);
}
if (subAnyElements != null && !subAnyElements.isEmpty())
{
for (ByteString s : subAnyElements)
{
buffer.append("*");
valueToFilterString(buffer, s);
}
}
buffer.append("*");
if (subFinalElement != null)
{
valueToFilterString(buffer, subFinalElement);
}
buffer.append(")");
break;
case GREATER_OR_EQUAL:
buffer.append("(");
buffer.append(attributeType);
buffer.append(">=");
valueToFilterString(buffer, assertionValue);
buffer.append(")");
break;
case LESS_OR_EQUAL:
buffer.append("(");
buffer.append(attributeType);
buffer.append("<=");
valueToFilterString(buffer, assertionValue);
buffer.append(")");
break;
case PRESENT:
buffer.append("(");
buffer.append(attributeType);
buffer.append("=*)");
break;
case APPROXIMATE_MATCH:
buffer.append("(");
buffer.append(attributeType);
buffer.append("~=");
valueToFilterString(buffer, assertionValue);
buffer.append(")");
break;
case EXTENSIBLE_MATCH:
buffer.append("(");
if (attributeType != null)
{
buffer.append(attributeType);
}
if (dnAttributes)
{
buffer.append(":dn");
}
if (matchingRuleID != null)
{
buffer.append(":");
buffer.append(matchingRuleID);
}
buffer.append(":=");
valueToFilterString(buffer, assertionValue);
buffer.append(")");
break;
}
}
/**
* Returns the {@code objectClass} presence filter {@code (objectClass=*)}.
*
* @return The {@code objectClass} presence filter {@code (objectClass=*)}.
*/
public static LDAPFilter objectClassPresent()
{
if (objectClassPresent == null)
{
try
{
objectClassPresent = LDAPFilter.decode("(objectclass=*)");
}
catch (LDAPException canNeverHappen)
{
logger.traceException(canNeverHappen);
}
}
return objectClassPresent;
}
}