All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.novell.ldap.rfc2251.RfcFilter Maven / Gradle / Ivy

/* **************************************************************************
 * $OpenLDAP$
 *
 * Copyright (C) 1999, 2000, 2001 Novell, Inc. All Rights Reserved.
 *
 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
 * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
 * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
 * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
 * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
 * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
 * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
 * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
 ******************************************************************************/

package com.novell.ldap.rfc2251;

import java.io.UnsupportedEncodingException;
import java.util.StringTokenizer;
import java.util.Stack;
import java.util.Iterator;

import com.novell.ldap.asn1.*;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPLocalException;
import com.novell.ldap.LDAPSearchRequest;
import com.novell.ldap.resources.*;

/**
 * Represents an LDAP Filter.
 *
 * 

This filter object can be created from a String or can be built up * programatically by adding filter components one at a time. Existing filter * components can be iterated though.

* *

Each filter component has an integer identifier defined in this class. * The following are basic filter components: {@link #EQUALITY_MATCH}, * {@link #GREATER_OR_EQUAL}, {@link #LESS_OR_EQUAL}, {@link #SUBSTRINGS}, * {@link #PRESENT}, {@link #APPROX_MATCH}, {@link #EXTENSIBLE_MATCH}.

* *

More filters can be nested together into more complex filters with the * following filter components: {@link #AND}, {@link #OR}, {@link #NOT}

* *

Substrings can have three components: *

 *       Filter ::= CHOICE {
 *               and             [0] SET OF Filter,
 *               or              [1] SET OF Filter,
 *               not             [2] Filter,
 *               equalityMatch   [3] AttributeValueAssertion,
 *               substrings      [4] SubstringFilter,
 *               greaterOrEqual  [5] AttributeValueAssertion,
 *               lessOrEqual     [6] AttributeValueAssertion,
 *               present         [7] AttributeDescription,
 *               approxMatch     [8] AttributeValueAssertion,
 *               extensibleMatch [9] MatchingRuleAssertion }
 *
*/ public class RfcFilter extends ASN1Choice { //************************************************************************* // Public variables for Filter //************************************************************************* /** * Identifier for AND component. */ public final static int AND = LDAPSearchRequest.AND; /** * Identifier for OR component. */ public final static int OR = LDAPSearchRequest.OR; /** * Identifier for NOT component. */ public final static int NOT = LDAPSearchRequest.NOT; /** * Identifier for EQUALITY_MATCH component. */ public final static int EQUALITY_MATCH = LDAPSearchRequest.EQUALITY_MATCH; /** * Identifier for SUBSTRINGS component. */ public final static int SUBSTRINGS = LDAPSearchRequest.SUBSTRINGS; /** * Identifier for GREATER_OR_EQUAL component. */ public final static int GREATER_OR_EQUAL = LDAPSearchRequest.GREATER_OR_EQUAL; /** * Identifier for LESS_OR_EQUAL component. */ public final static int LESS_OR_EQUAL = LDAPSearchRequest.LESS_OR_EQUAL; /** * Identifier for PRESENT component. */ public final static int PRESENT = LDAPSearchRequest.PRESENT; /** * Identifier for APPROX_MATCH component. */ public final static int APPROX_MATCH = LDAPSearchRequest.APPROX_MATCH; /** * Identifier for EXTENSIBLE_MATCH component. */ public final static int EXTENSIBLE_MATCH = LDAPSearchRequest.EXTENSIBLE_MATCH; /** * Identifier for INITIAL component. */ public final static int INITIAL = LDAPSearchRequest.INITIAL; /** * Identifier for ANY component. */ public final static int ANY = LDAPSearchRequest.ANY; /** * Identifier for FINAL component. */ public final static int FINAL = LDAPSearchRequest.FINAL; //************************************************************************* // Private variables for Filter //************************************************************************* private FilterTokenizer ft; private Stack filterStack; private boolean finalFound; //************************************************************************* // Constructor for Filter //************************************************************************* /** * Constructs a Filter object by parsing an RFC 2254 Search Filter String. */ public RfcFilter(String filter) throws LDAPException { super(null); setChoiceValue(parse(filter)); return; } /** * Constructs a Filter object that will be built up piece by piece. */ public RfcFilter() { super(null); filterStack = new Stack(); //The choice value must be set later: setChoiceValue(rootFilterTag) return; } //************************************************************************* // Helper methods for RFC 2254 Search Filter parsing. //************************************************************************* /** * Parses an RFC 2251 filter string into an ASN.1 LDAP Filter object. */ private final ASN1Tagged parse(String filterExpr) throws LDAPException { if(filterExpr == null || filterExpr.equals("")) { filterExpr = new String("(objectclass=*)"); } int idx; if( (idx = filterExpr.indexOf('\\')) != -1) { StringBuffer sb = new StringBuffer(filterExpr); int i = idx; while(i < (sb.length()-1)) { char c = sb.charAt(i++); if (c == '\\') { // found '\' (backslash) // If V2 escape, turn to a V3 escape c = sb.charAt(i); if(c =='*' || c =='(' || c==')' || c =='\\' ) { // LDAP v2 filter, convert them into hex chars sb.delete(i,i+1); sb.insert(i, Integer.toHexString((int)c)); i+=2; } } } filterExpr = sb.toString(); } // missing opening and closing parentheses, must be V2, add parentheses if( (filterExpr.charAt(0) != '(') && (filterExpr.charAt(filterExpr.length()-1) != ')')) { filterExpr = "(" + filterExpr + ")"; } char ch = filterExpr.charAt(0); int len = filterExpr.length(); // missing opening parenthesis ? if (ch!='(') { throw new LDAPLocalException( ExceptionMessages.MISSING_LEFT_PAREN, LDAPException.FILTER_ERROR); } // missing closing parenthesis ? if( filterExpr.charAt(len-1) != ')') { throw new LDAPLocalException( ExceptionMessages.MISSING_RIGHT_PAREN, LDAPException.FILTER_ERROR); } // unmatched parentheses ? int parenCount = 0; for (int i=0; i 0) { throw new LDAPLocalException( ExceptionMessages.MISSING_RIGHT_PAREN, LDAPException.FILTER_ERROR); } if (parenCount < 0) { throw new LDAPLocalException( ExceptionMessages.MISSING_LEFT_PAREN, LDAPException.FILTER_ERROR); } ft = new FilterTokenizer(filterExpr); return parseFilter(); } /** * Parses an RFC 2254 filter */ private final ASN1Tagged parseFilter() throws LDAPException { ft.getLeftParen(); ASN1Tagged filter = parseFilterComp(); ft.getRightParen(); return filter; } /** * RFC 2254 filter helper method. Will Parse a filter component. */ private final ASN1Tagged parseFilterComp() throws LDAPException { ASN1Tagged tag = null; int filterComp = ft.getOpOrAttr(); switch(filterComp) { case AND: case OR: tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, filterComp), parseFilterList(), false); break; case NOT: tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, filterComp), parseFilter(), true); break; default: int filterType = ft.getFilterType(); String value = ft.getValue(); switch(filterType) { case GREATER_OR_EQUAL: case LESS_OR_EQUAL: case APPROX_MATCH: tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, filterType), new RfcAttributeValueAssertion( new RfcAttributeDescription(ft.getAttr()), new RfcAssertionValue(unescapeString(value))), false); break; case EQUALITY_MATCH: // may be PRESENT or SUBSTRINGS also if(value.equals("*")) { // present tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, PRESENT), new RfcAttributeDescription(ft.getAttr()), false); } else if(value.indexOf('*') != -1) { // substrings parse: // [initial], *any*, [final] into an ASN1SequenceOf StringTokenizer sub = new StringTokenizer(value, "*", true); ASN1SequenceOf seq = new ASN1SequenceOf(5); int tokCnt = sub.countTokens(); int cnt = 0; String lastTok = new String(""); while(sub.hasMoreTokens()) { String subTok = sub.nextToken(); cnt++; if(subTok.equals("*")) { // if previous token was '*', and since the current // token is a '*', we need to insert 'any' if (lastTok.equals(subTok)) { // '**' seq.add( new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, ANY), new RfcLDAPString(unescapeString("")), false)); } } else { // value (RfcLDAPString) if(cnt == 1) { // initial seq.add( new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, INITIAL), new RfcLDAPString(unescapeString(subTok)), false)); } else if(cnt < tokCnt) { // any seq.add( new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, ANY), new RfcLDAPString(unescapeString(subTok)), false)); } else { // final seq.add( new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, FINAL), new RfcLDAPString(unescapeString(subTok)), false)); } } lastTok = subTok; } tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, SUBSTRINGS), new RfcSubstringFilter( new RfcAttributeDescription(ft.getAttr()), seq), false); } else { // simple tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, EQUALITY_MATCH), new RfcAttributeValueAssertion( new RfcAttributeDescription(ft.getAttr()), new RfcAssertionValue(unescapeString(value))), false); } break; case EXTENSIBLE_MATCH: String type = null, matchingRule = null; boolean dnAttributes = false; StringTokenizer st = new StringTokenizer(ft.getAttr(), ":", true); boolean first = true; while(st.hasMoreTokens()) { String s = st.nextToken().trim(); if(first && !s.equals(":")) { type = s; } else // dn must be lower case to be considered dn of the Entry. if(s.equals("dn")) { dnAttributes = true; } else if(!s.equals(":")) { matchingRule = s; } first = false; } tag = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, EXTENSIBLE_MATCH), new RfcMatchingRuleAssertion( (matchingRule == null) ? null : new RfcMatchingRuleId(matchingRule), (type == null) ? null : new RfcAttributeDescription(type), new RfcAssertionValue(unescapeString(value)), (dnAttributes == false) ? null : new ASN1Boolean(true)), false); } } return tag; } /** * Must have 1 or more Filters */ private final ASN1SetOf parseFilterList() throws LDAPException { ASN1SetOf set = new ASN1SetOf(); set.add(parseFilter()); // must have at least 1 filter while(ft.peekChar() == '(') { // check for more filters set.add(parseFilter()); } return set; } /** * Convert hex character to an integer. Return -1 if char is something * other than a hex char. */ static final int hex2int(char c) { return (c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : -1; } /** * Replace escaped hex digits with the equivalent binary representation. * Assume either V2 or V3 escape mechanisms: * V2: \*, \(, \), \\. * V3: \2A, \28, \29, \5C, \00. * * @param string A part of the input filter string to be converted. * * @return octet-string encoding of the specified string. */ private final byte[] unescapeString(String string) throws LDAPException { // give octets enough space to grow byte octets[] = new byte[string.length()*3]; // index for string and octets int iString, iOctets; // escape==true means we are in an escape sequence. boolean escape = false; // escStart==true means we are reading the first character of an escape. boolean escStart = false; int ival, length = string.length(); byte utf8Bytes[]; char ch; // Character we are adding to the octet string char ca[] = new char[1]; // used while converting multibyte UTF-8 char char temp = 0; // holds the value of the escaped sequence // loop through each character of the string and copy them into octets // converting escaped sequences when needed for(iString = 0, iOctets = 0; iString < length; iString++) { ch = string.charAt(iString); if(escape) { if((ival = hex2int(ch)) < 0) { // Invalid escape value(not a hex character) throw new LDAPLocalException( ExceptionMessages.INVALID_ESCAPE, new Object[] {new Character(ch)}, LDAPException.FILTER_ERROR); } else { // V3 escaped: \\** if(escStart) { temp = (char)(ival<<4); // high bits of escaped char escStart = false; } else { temp |= (char)(ival); // all bits of escaped char octets[iOctets++] = (byte) temp; escStart = escape = false; } } } else if(ch == '\\') { escStart = escape = true; } else { try { // place the character into octets. if (( ch >= 0x01 && ch <= 0x27 ) || ( ch >= 0x2B && ch <= 0x5B ) || ( ch >= 0x5D )) { // found valid char if (ch <= 0x7f) { // char = %x01-27 / %x2b-5b / %x5d-7f octets[iOctets++] = (byte)ch; } else { // char > 0x7f, could be encoded in 2 or 3 bytes ca[0] = ch; utf8Bytes = new String(ca).getBytes("UTF-8"); // copy utf8 encoded character into octets System.arraycopy(utf8Bytes, 0, octets, iOctets, utf8Bytes.length); iOctets = iOctets + utf8Bytes.length; } escape = false; } else { // found invalid character String escString = ""; ca[0] = ch; utf8Bytes = new String(ca).getBytes("UTF-8"); for( int i=0; i < utf8Bytes.length; i++) { byte u = utf8Bytes[i]; if( (u >= 0) && (u < 0x10)) { escString = escString + "\\0" + Integer.toHexString(u & 0xff); } else { escString = escString + "\\" + Integer.toHexString(u & 0xff); } } throw new LDAPLocalException( ExceptionMessages.INVALID_CHAR_IN_FILTER, new Object[] { new Character(ch), escString }, LDAPException.FILTER_ERROR); } } catch ( UnsupportedEncodingException ue) { throw new RuntimeException( "UTF-8 String encoding not supported by JVM"); } } } // Verify that any escape sequence completed if (escStart || escape) { throw new LDAPLocalException(ExceptionMessages.SHORT_ESCAPE, LDAPException.FILTER_ERROR); } byte toReturn[] = new byte[iOctets]; System.arraycopy(octets, 0, toReturn, 0, iOctets); octets = null; return toReturn; } /* ********************************************************************** * The following methods aid in building filters sequentially, * and is used by DSMLHandler: ***********************************************************************/ /** * Called by sequential filter building methods to add to a filter * component. * *

Verifies that the specified ASN1Object can be added, then adds the * object to the filter.

* @param current Filter component to be added to the filter * @throws LDAPLocalException Occurs when an invalid component is added, or * when the component is out of sequence. */ private void addObject(ASN1Object current) throws LDAPLocalException { if (filterStack == null){ filterStack = new Stack(); } if ( choiceValue() == null ) { //ChoiceValue is the root ASN1 node setChoiceValue(current); } else { ASN1Tagged topOfStack = (ASN1Tagged)filterStack.peek(); ASN1Object value = topOfStack.taggedValue(); if (value == null){ topOfStack.setTaggedValue(current); filterStack.add(current); } else if (value instanceof ASN1SetOf) { ((ASN1SetOf)value).add( current ); //don't add this to the stack: } else if (value instanceof ASN1Set) { ((ASN1Set)value).add( current ); //don't add this to the stack: } else if (value.getIdentifier().getTag() == LDAPSearchRequest.NOT){ throw new LDAPLocalException( "Attemp to create more than one 'not' sub-filter", LDAPException.FILTER_ERROR); } } int type = current.getIdentifier().getTag(); if (type == AND || type == OR || type == NOT ){ filterStack.add(current); } return; } /** * Creates and addes a substrings filter component. * *

startSubstrings must be immediatly followed by at least one * {@link #addSubstring} method and one {@link #endSubstrings} method

* @throws com.novell.ldap.LDAPLocalException * Occurs when this component is created out of sequence. */ public void startSubstrings(String attrName) throws LDAPLocalException { finalFound = false; ASN1SequenceOf seq = new ASN1SequenceOf(5); ASN1Object current = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, SUBSTRINGS), new RfcSubstringFilter( new RfcAttributeDescription(attrName), seq), //this sequence will be filled in later false); addObject(current); filterStack.push(seq); return; } /** * Adds a Substring component of initial, any or final substring matching. * *

This method can be invoked only if startSubString was the last filter- * building method called. A substring is not required to have an 'INITIAL' * substring. However, when a filter contains an 'INITIAL' substring only * one can be added, and it must be the first substring added. Any number of * 'ANY' substrings can be added. A substring is not required to have a * 'FINAL' substrings either. However, when a filter does contain a 'FINAL' * substring only one can be added, and it must be the last substring added. *

* @param type Substring type: INITIAL | ANY | FINAL] * @param value Value to use for matching * @throws LDAPLocalException Occurs if this method is called out of * sequence or the type added is out of sequence. */ public void addSubstring(int type, byte[] value) throws LDAPLocalException { try { ASN1SequenceOf substringSeq = (ASN1SequenceOf)filterStack.peek(); if (type != INITIAL && type != ANY && type != FINAL){ throw new LDAPLocalException("Attempt to add an invalid " + "substring type", LDAPException.FILTER_ERROR); } if (type == INITIAL && substringSeq.size() !=0) { throw new LDAPLocalException("Attempt to add an initial " + "substring match after the first substring", LDAPException.FILTER_ERROR); } if (finalFound){ throw new LDAPLocalException("Attempt to add a substring " + "match after a final substring match", LDAPException.FILTER_ERROR); } if (type == FINAL){ finalFound = true; } substringSeq.add( new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, type), new RfcLDAPString(value), false)); } catch (ClassCastException e){ throw new LDAPLocalException("A call to addSubstring occured " + "without calling startSubstring", LDAPException.FILTER_ERROR); } return; } /** * Completes a SubString filter component. * * @throws LDAPLocalException Occurs when this is called out of sequence, * or the substrings filter is empty. */ public void endSubstrings() throws LDAPLocalException { try { finalFound = false; ASN1SequenceOf substringSeq = (ASN1SequenceOf) filterStack.peek(); if (substringSeq.size() == 0){ throw new LDAPLocalException("Empty substring filter", LDAPException.FILTER_ERROR); } } catch (ClassCastException e){ throw new LDAPLocalException("Missmatched ending of substrings", LDAPException.FILTER_ERROR); } filterStack.pop(); return; } /** * Creates and adds an AttributeValueAssertion to the filter. * * @param rfcType Filter type: EQUALITY_MATCH | GREATER_OR_EQUAL * | LESS_OR_EQUAL | APPROX_MATCH ] * @param attrName Name of the attribute to be asserted * @param value Value of the attribute to be asserted * @throws LDAPLocalException * Occurs when the filter type is not a valid attribute assertion. */ public void addAttributeValueAssertion(int rfcType, String attrName, byte[] value) throws LDAPLocalException { if (filterStack != null && !filterStack.empty() && filterStack.peek() instanceof ASN1SequenceOf) { //If a sequenceof is on the stack then substring is left on the stack throw new LDAPLocalException( "Cannot insert an attribute assertion in a substring", LDAPException.FILTER_ERROR); } if ((rfcType != EQUALITY_MATCH) && (rfcType != GREATER_OR_EQUAL) && (rfcType != LESS_OR_EQUAL) && (rfcType != APPROX_MATCH)) { throw new LDAPLocalException( "Invalid filter type for AttributeValueAssertion", LDAPException.FILTER_ERROR); } ASN1Object current = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, rfcType), new RfcAttributeValueAssertion( new RfcAttributeDescription(attrName), new RfcAssertionValue(value)), false); addObject(current); return; } /** * Creates and adds a present matching to the filter. * * @param attrName Name of the attribute to check for presence. * @throws LDAPLocalException * Occurs if addPresent is called out of sequence. */ public void addPresent(String attrName) throws LDAPLocalException { ASN1Object current = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, false, PRESENT), new RfcAttributeDescription(attrName), false); addObject(current); return; } /** * Adds an extensible match to the filter. * * @param matchingRule * OID or name of the matching rule to use for comparison * @param attrName Name of the attribute to match. * @param value Value of the attribute to match against. * @param useDNMatching Indicates whether DN matching should be used. * @throws LDAPLocalException * Occurs when addExtensibleMatch is called out of sequence. */ public void addExtensibleMatch(String matchingRule, String attrName, byte[] value, boolean useDNMatching) throws LDAPLocalException { ASN1Object current = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, EXTENSIBLE_MATCH), new RfcMatchingRuleAssertion( (matchingRule==null) ? null:new RfcMatchingRuleId(matchingRule), (attrName==null) ? null:new RfcAttributeDescription(attrName), new RfcAssertionValue(value), (useDNMatching == false) ? null : new ASN1Boolean(true)), false); addObject(current); return; } /** * Creates and adds the ASN1Tagged value for a nestedFilter: AND, OR, or * NOT. * *

Note that a Not nested filter can only have one filter, where AND * and OR do not

* * @param rfcType Filter type: * [AND | OR | NOT] * @throws com.novell.ldap.LDAPLocalException */ public void startNestedFilter(int rfcType) throws LDAPLocalException { ASN1Object current; if (rfcType == AND || rfcType == OR){ current = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, rfcType), new ASN1SetOf(), //content to be set later false); } else if (rfcType == NOT){ current = new ASN1Tagged( new ASN1Identifier(ASN1Identifier.CONTEXT, true, rfcType), null, //content to be set later true); } else { throw new LDAPLocalException( "Attempt to create a nested filter other than AND, OR or NOT", LDAPException.FILTER_ERROR); } addObject(current); return; } /** * Completes a nested filter and checks for the valid filter type. * @param rfcType Type of filter to complete. * @throws com.novell.ldap.LDAPLocalException Occurs when the specified * type differs from the current filter component. */ public void endNestedFilter(int rfcType) throws LDAPLocalException { if (rfcType == NOT) { //if this is a Not than Not should be the second thing on the stack filterStack.pop(); } int topOfStackType = ((ASN1Object) filterStack.peek()).getIdentifier().getTag(); if (topOfStackType != rfcType){ throw new LDAPLocalException("Missmatched ending of nested filter", LDAPException.FILTER_ERROR); } filterStack.pop(); return; } /** * Creates an iterator over the preparsed segments of a filter. * *

The first object returned by an iterator is an integer indicating the * type of filter components. Subseqence values are returned. If a * component is of type 'AND' or 'OR' or 'NOT' then the value * returned is another iterator. This iterator is used by toString.

* * @return Iterator over filter segments */ public Iterator getFilterIterator(){ return new FilterIterator((ASN1Tagged)this.choiceValue()); } /** * Creates and returns a String representation of this filter. */ public String filterToString(){ StringBuffer filter = new StringBuffer(); stringFilter(this.getFilterIterator(), filter); return filter.toString(); } /** * Uses a filterIterator to create a string representation of a filter. * * @param itr Iterator of filter components * @param filter Buffer to place a string representation of the filter * @see #getFilterIterator */ private static void stringFilter(Iterator itr, StringBuffer filter) { int op=-1; filter.append('('); while (itr.hasNext()){ Object filterpart = itr.next(); if (filterpart instanceof Integer){ op = ((Integer)filterpart).intValue(); switch (op){ case AND: filter.append('&'); break; case OR: filter.append('|'); break; case NOT: filter.append('!'); break; case EQUALITY_MATCH:{ filter.append((String)itr.next()); filter.append('='); byte[] value = (byte[])itr.next(); filter.append(byteString(value)); break; } case GREATER_OR_EQUAL:{ filter.append((String)itr.next()); filter.append(">="); byte[] value = (byte[])itr.next(); filter.append(byteString(value)); break; } case LESS_OR_EQUAL:{ filter.append((String)itr.next()); filter.append("<="); byte[] value = (byte[])itr.next(); filter.append(byteString(value)); break; } case PRESENT: filter.append((String)itr.next()); filter.append("=*"); break; case APPROX_MATCH: filter.append((String)itr.next()); filter.append("~="); byte[] value = (byte[])itr.next(); filter.append(byteString(value)); break; case EXTENSIBLE_MATCH: String oid = (String)itr.next(); filter.append((String)itr.next()); filter.append(':'); filter.append(oid); filter.append(":="); filter.append((String)itr.next()); break; case SUBSTRINGS:{ filter.append((String)itr.next()); filter.append('='); boolean noStarLast = false; while (itr.hasNext()){ op = ((Integer)itr.next()).intValue(); switch(op){ case INITIAL: filter.append((String)itr.next()); filter.append('*'); noStarLast = false; break; case ANY: if( noStarLast) filter.append('*'); filter.append((String)itr.next()); filter.append('*'); noStarLast = false; break; case FINAL: if( noStarLast) filter.append('*'); filter.append((String)itr.next()); break; } } break; } } } else if (filterpart instanceof Iterator){ stringFilter((Iterator)filterpart, filter); } } filter.append(')'); } /** * Convert a UTF8 encoded string, or binary data, into a String encoded for * a string filter. */ private static String byteString(byte[] value) { String toReturn = null; if (com.novell.ldap.util.Base64.isValidUTF8(value, true)) { try { toReturn = new String(value, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException( "Default JVM does not support UTF-8 encoding" + e); } } else { StringBuffer binary = new StringBuffer(); for (int i=0; i=0) { //one character hex string binary.append("\\0"); binary.append(Integer.toHexString(value[i])); } else { //negative (eight character) hex string binary.append("\\"+ Integer.toHexString(value[i]).substring(6)); } } toReturn = binary.toString(); } return toReturn; } /** * This inner class wrappers the Search Filter with an iterator. * This iterator will give access to all the individual components * preparsed. The first call to next will return an Integer identifying * the type of filter component. Then the component values will be returned * AND, NOT, and OR components values will be returned as Iterators. */ private class FilterIterator implements Iterator{ ASN1Tagged root; /** indicates if the identifier for a component has been returned yet */ boolean tagReturned = false; /** indexes the several parts a component may have */ int index = -1; private boolean hasMore = true; public FilterIterator(ASN1Tagged root){ this.root = root; } public boolean hasNext() { return hasMore; } /** * Returns filter identifiers and components of a filter. * * The first object returned is an Integer identifying * its type. */ public Object next() { Object toReturn = null; if (!tagReturned){ tagReturned = true; toReturn = new Integer(root.getIdentifier().getTag()); } else { ASN1Object asn1 = root.taggedValue(); if (asn1 instanceof RfcLDAPString){ //one value to iterate hasMore = false; toReturn = ((RfcLDAPString)asn1).stringValue(); } else if (asn1 instanceof RfcSubstringFilter){ RfcSubstringFilter sub = (RfcSubstringFilter) asn1; if (index == -1){ //return attribute name index=0; RfcAttributeDescription attr = (RfcAttributeDescription) sub.get(0); toReturn = attr.stringValue(); } else if (index % 2 == 0) { //return substring identifier ASN1SequenceOf substrs = (ASN1SequenceOf) sub.get(1); toReturn = new Integer( ((ASN1Tagged) substrs.get(index/2)).getIdentifier().getTag()); index++; } else { //return substring value ASN1SequenceOf substrs = (ASN1SequenceOf) sub.get(1); ASN1Tagged tag = (ASN1Tagged)substrs.get(index/2); RfcLDAPString value = (RfcLDAPString) tag.taggedValue(); toReturn = value.stringValue(); index++; } if (index / 2 >= ((ASN1SequenceOf) sub.get(1)).size()){ hasMore = false; } } else if (asn1 instanceof RfcAttributeValueAssertion){ // components: =,>=,<=,~= RfcAttributeValueAssertion assertion = (RfcAttributeValueAssertion)asn1; if (index == -1){ toReturn = assertion.getAttributeDescription(); index = 1; } else if (index == 1){ toReturn = assertion.getAssertionValue(); index = 2; hasMore = false; } } else if (asn1 instanceof RfcMatchingRuleAssertion){ //Extensible match RfcMatchingRuleAssertion exMatch = (RfcMatchingRuleAssertion) asn1; if (index == -1){ index = 0; } toReturn = ((ASN1OctetString) ((ASN1Tagged) exMatch.get(index++) ).taggedValue() ).stringValue(); if (index > 2){ hasMore = false; } } else if (asn1 instanceof ASN1SetOf){ //AND and OR nested components ASN1SetOf set = (ASN1SetOf)asn1; if (index == -1){ index = 0; } toReturn = new FilterIterator((ASN1Tagged)set.get(index++)); if (index >= set.size()){ this.hasMore = false; } } else if (asn1 instanceof ASN1Tagged) { //NOT nested component. toReturn = new FilterIterator((ASN1Tagged)asn1); this.hasMore = false; } } return toReturn; } public void remove() { throw new java.lang.UnsupportedOperationException( "Remove is not supported on a filter iterator"); } } /** * This inner class will tokenize the components of an RFC 2254 search filter. */ class FilterTokenizer implements java.io.Serializable { //************************************************************************* // Private variables //************************************************************************* private String filter; // The filter string to parse private String attr; // Name of the attribute just parsed private int offset; // Offset pointer into the filter string private int filterLength; // Length of the filter string to parse //************************************************************************* // Constructor //************************************************************************* /** * Constructs a FilterTokenizer for a filter. */ public FilterTokenizer(String filter) { this.filter = filter; this.offset = 0; this.filterLength = filter.length(); return; } //************************************************************************* // Tokenizer methods //************************************************************************* /** * Reads the current char and throws an Exception if it is not a left * parenthesis. */ public final void getLeftParen() throws LDAPException { if(offset >= filterLength) { //"Unexpected end of filter", throw new LDAPLocalException(ExceptionMessages.UNEXPECTED_END, LDAPException.FILTER_ERROR); } if(filter.charAt(offset++) != '(') { //"Missing left paren", throw new LDAPLocalException(ExceptionMessages.EXPECTING_LEFT_PAREN, new Object[] { new Character( filter.charAt(offset-=1)) }, LDAPException.FILTER_ERROR); } return; } /** * Reads the current char and throws an Exception if it is not a right * parenthesis. */ public final void getRightParen() throws LDAPException { if(offset >= filterLength) { //"Unexpected end of filter", throw new LDAPLocalException(ExceptionMessages.UNEXPECTED_END, LDAPException.FILTER_ERROR); } if(filter.charAt(offset++) != ')') { //"Missing right paren", throw new LDAPLocalException(ExceptionMessages.EXPECTING_RIGHT_PAREN, new Object[] { new Character(filter.charAt(offset-1)) }, LDAPException.FILTER_ERROR); } return; } /** * Reads either an operator, or an attribute, whichever is * next in the filter string. * *

Operators are &, |, or !.

* *

If the next component is an attribute, it is read and stored in the * attr field of this class which may be retrieved with getAttr() * and a -1 is returned. Otherwise, the int value of the operator read is * returned.

*/ public final int getOpOrAttr() throws LDAPException { int index; if(offset >= filterLength) { //"Unexpected end of filter", throw new LDAPLocalException(ExceptionMessages.UNEXPECTED_END, LDAPException.FILTER_ERROR); } int ret; int testChar = filter.charAt(offset); if(testChar == '&') { offset++; ret = AND; } else if(testChar == '|') { offset++; ret = OR; } else if(testChar == '!') { offset++; ret = NOT; } else { if (filter.startsWith(":=", offset) == true) { throw new LDAPLocalException( ExceptionMessages.NO_MATCHING_RULE, LDAPException.FILTER_ERROR); } if (filter.startsWith("::=", offset) == true || filter.startsWith(":::=", offset) == true ) { throw new LDAPLocalException( ExceptionMessages.NO_DN_NOR_MATCHING_RULE, LDAPException.FILTER_ERROR); } // get first component of 'item' (attr or :dn or :matchingrule) String delims = "=~<>()"; StringBuffer sb = new StringBuffer(); while(delims.indexOf(filter.charAt(offset)) == -1 && filter.startsWith(":=", offset) == false) { sb.append(filter.charAt(offset++)); } attr = sb.toString().trim(); // is there an attribute name specified in the filter ? if (attr.length() == 0 || attr.charAt(0) == ';') { throw new LDAPLocalException( ExceptionMessages.NO_ATTRIBUTE_NAME, LDAPException.FILTER_ERROR); } for (index=0; index= filterLength) { //"Unexpected end of filter", throw new LDAPLocalException(ExceptionMessages.UNEXPECTED_END, LDAPException.FILTER_ERROR); } int ret; if(filter.startsWith(">=", offset)) { offset+=2; ret = GREATER_OR_EQUAL; } else if(filter.startsWith("<=", offset)) { offset+=2; ret = LESS_OR_EQUAL; } else if(filter.startsWith("~=", offset)) { offset+=2; ret = APPROX_MATCH; } else if(filter.startsWith(":=", offset)) { offset+=2; ret = EXTENSIBLE_MATCH; } else if(filter.charAt(offset) == '=') { offset++; ret = EQUALITY_MATCH; } else { //"Invalid comparison operator", throw new LDAPLocalException( ExceptionMessages.INVALID_FILTER_COMPARISON, LDAPException.FILTER_ERROR); } return ret; } /** * Reads a value from a filter string. */ public final String getValue() throws LDAPException { if(offset >= filterLength) { //"Unexpected end of filter", throw new LDAPLocalException(ExceptionMessages.UNEXPECTED_END, LDAPException.FILTER_ERROR); } int idx = filter.indexOf( ')', offset); if( idx == -1) { idx = filterLength; } String ret = filter.substring( offset, idx); offset = idx; return ret; } /** * Returns the current attribute identifier. */ public final String getAttr() { return attr; } /** * Return the current char without advancing the offset pointer. This is * used by ParseFilterList when determining if there are any more * Filters in the list. */ public final char peekChar() throws LDAPException { if(offset >= filterLength) { //"Unexpected end of filter", throw new LDAPLocalException(ExceptionMessages.UNEXPECTED_END, LDAPException.FILTER_ERROR); } return filter.charAt(offset); } /** * Writes the object state to a stream in standard Default Binary format * This function wraps ObjectOutputStream' s defaultWriteObject() to write * the non-static and non-transient fields of the current class to the stream * * @param objectOStrm The OutputSteam where the Object need to be written */ private void writeObject(java.io.ObjectOutputStream objectOStrm) throws java.io.IOException { objectOStrm.defaultWriteObject(); } /** * Reads the serialized object from the underlying input stream. * This function wraps ObjectInputStream's defaultReadObject() function * * @param objectIStrm InputStream used to recover those objects previously serialized. */ private void readObject(java.io.ObjectInputStream objectIStrm) throws java.io.IOException, ClassNotFoundException { objectIStrm.defaultReadObject(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy