1 /******************************************************************************
3 * Copyright (c) 2003 Novell Inc. www.novell.com
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 *******************************************************************************/
24 // Novell.Directory.Ldap.Rfc2251.RfcFilter.cs
27 // Sunil Kumar (Sunilk@novell.com)
29 // (C) 2003 Novell, Inc (http://www.novell.com)
33 using Novell.Directory.Ldap.Asn1;
34 using LdapException = Novell.Directory.Ldap.LdapException;
35 using LdapLocalException = Novell.Directory.Ldap.LdapLocalException;
36 using LdapSearchRequest = Novell.Directory.Ldap.LdapSearchRequest;
37 using Novell.Directory.Ldap.Utilclass;
39 namespace Novell.Directory.Ldap.Rfc2251
42 /// <summary> Represents an Ldap Filter.
44 /// This filter object can be created from a String or can be built up
45 /// programatically by adding filter components one at a time. Existing filter
46 /// components can be iterated though.
48 /// Each filter component has an integer identifier defined in this class.
49 /// The following are basic filter components: {@link #EQUALITY_MATCH},
50 /// {@link #GREATER_OR_EQUAL}, {@link #LESS_OR_EQUAL}, {@link #SUBSTRINGS},
51 /// {@link #PRESENT}, {@link #APPROX_MATCH}, {@link #EXTENSIBLE_MATCH}.
53 /// More filters can be nested together into more complex filters with the
54 /// following filter components: {@link #AND}, {@link #OR}, {@link #NOT}
56 /// Substrings can have three components:
58 /// Filter ::= CHOICE {
59 /// and [0] SET OF Filter,
60 /// or [1] SET OF Filter,
62 /// equalityMatch [3] AttributeValueAssertion,
63 /// substrings [4] SubstringFilter,
64 /// greaterOrEqual [5] AttributeValueAssertion,
65 /// lessOrEqual [6] AttributeValueAssertion,
66 /// present [7] AttributeDescription,
67 /// approxMatch [8] AttributeValueAssertion,
68 /// extensibleMatch [9] MatchingRuleAssertion }
71 public class RfcFilter:Asn1Choice
73 //*************************************************************************
74 // Public variables for Filter
75 //*************************************************************************
77 /// <summary> Identifier for AND component.</summary>
78 public const int AND = LdapSearchRequest.AND;
79 /// <summary> Identifier for OR component.</summary>
80 public const int OR = LdapSearchRequest.OR;
81 /// <summary> Identifier for NOT component.</summary>
82 public const int NOT = LdapSearchRequest.NOT;
83 /// <summary> Identifier for EQUALITY_MATCH component.</summary>
84 public const int EQUALITY_MATCH = LdapSearchRequest.EQUALITY_MATCH;
85 /// <summary> Identifier for SUBSTRINGS component.</summary>
86 public const int SUBSTRINGS = LdapSearchRequest.SUBSTRINGS;
87 /// <summary> Identifier for GREATER_OR_EQUAL component.</summary>
88 public const int GREATER_OR_EQUAL = LdapSearchRequest.GREATER_OR_EQUAL;
89 /// <summary> Identifier for LESS_OR_EQUAL component.</summary>
90 public const int LESS_OR_EQUAL = LdapSearchRequest.LESS_OR_EQUAL;
91 /// <summary> Identifier for PRESENT component.</summary>
92 public const int PRESENT = LdapSearchRequest.PRESENT;
93 /// <summary> Identifier for APPROX_MATCH component.</summary>
94 public const int APPROX_MATCH = LdapSearchRequest.APPROX_MATCH;
95 /// <summary> Identifier for EXTENSIBLE_MATCH component.</summary>
96 public const int EXTENSIBLE_MATCH = LdapSearchRequest.EXTENSIBLE_MATCH;
98 /// <summary> Identifier for INITIAL component.</summary>
99 public const int INITIAL = LdapSearchRequest.INITIAL;
100 /// <summary> Identifier for ANY component.</summary>
101 public const int ANY = LdapSearchRequest.ANY;
102 /// <summary> Identifier for FINAL component.</summary>
103 public const int FINAL = LdapSearchRequest.FINAL;
105 //*************************************************************************
106 // Private variables for Filter
107 //*************************************************************************
109 private FilterTokenizer ft;
110 private System.Collections.Stack filterStack;
111 private bool finalFound;
113 //*************************************************************************
114 // Constructor for Filter
115 //*************************************************************************
117 /// <summary> Constructs a Filter object by parsing an RFC 2254 Search Filter String.</summary>
118 public RfcFilter(System.String filter):base(null)
120 ChoiceValue = parse(filter);
124 /// <summary> Constructs a Filter object that will be built up piece by piece. </summary>
125 public RfcFilter():base(null)
127 filterStack = new System.Collections.Stack();
128 //The choice value must be set later: setChoiceValue(rootFilterTag)
132 //*************************************************************************
133 // Helper methods for RFC 2254 Search Filter parsing.
134 //*************************************************************************
136 /// <summary> Parses an RFC 2251 filter string into an ASN.1 Ldap Filter object.</summary>
137 private Asn1Tagged parse(System.String filterExpr)
140 if ((System.Object) filterExpr == null || filterExpr.Equals(""))
142 filterExpr = new System.Text.StringBuilder("(objectclass=*)").ToString();
145 if ((idx = filterExpr.IndexOf((System.Char) '\\')) != - 1)
147 System.Text.StringBuilder sb = new System.Text.StringBuilder(filterExpr);
149 while (i < (sb.Length - 1))
154 // found '\' (backslash)
155 // If V2 escape, turn to a V3 escape
157 if (c == '*' || c == '(' || c == ')' || c == '\\')
159 // Ldap v2 filter, convert them into hex chars
160 sb.Remove(i, i + 1 - i);
161 sb.Insert(i, System.Convert.ToString((int) c, 16));
166 filterExpr = sb.ToString();
169 // missing opening and closing parentheses, must be V2, add parentheses
170 if ((filterExpr[0] != '(') && (filterExpr[filterExpr.Length - 1] != ')'))
172 filterExpr = "(" + filterExpr + ")";
175 char ch = filterExpr[0];
176 int len = filterExpr.Length;
178 // missing opening parenthesis ?
181 throw new LdapLocalException(ExceptionMessages.MISSING_LEFT_PAREN, LdapException.FILTER_ERROR);
184 // missing closing parenthesis ?
185 if (filterExpr[len - 1] != ')')
187 throw new LdapLocalException(ExceptionMessages.MISSING_RIGHT_PAREN, LdapException.FILTER_ERROR);
190 // unmatched parentheses ?
192 for (int i = 0; i < len; i++)
194 if (filterExpr[i] == '(')
199 if (filterExpr[i] == ')')
207 throw new LdapLocalException(ExceptionMessages.MISSING_RIGHT_PAREN, LdapException.FILTER_ERROR);
212 throw new LdapLocalException(ExceptionMessages.MISSING_LEFT_PAREN, LdapException.FILTER_ERROR);
214 ft = new FilterTokenizer(this, filterExpr);
216 return parseFilter();
219 /// <summary> Parses an RFC 2254 filter</summary>
220 private Asn1Tagged parseFilter()
224 Asn1Tagged filter = parseFilterComp();
231 /// <summary> RFC 2254 filter helper method. Will Parse a filter component.</summary>
232 private Asn1Tagged parseFilterComp()
234 Asn1Tagged tag = null;
235 int filterComp = ft.OpOrAttr;
242 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, filterComp), parseFilterList(), false);
246 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, filterComp), parseFilter(), true);
250 int filterType = ft.FilterType;
251 System.String value_Renamed = ft.Value;
256 case GREATER_OR_EQUAL:
259 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, filterType), new RfcAttributeValueAssertion(new RfcAttributeDescription(ft.Attr), new RfcAssertionValue(unescapeString(value_Renamed))), false);
263 if (value_Renamed.Equals("*"))
266 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, PRESENT), new RfcAttributeDescription(ft.Attr), false);
268 else if (value_Renamed.IndexOf((System.Char) '*') != - 1)
271 // [initial], *any*, [final] into an Asn1SequenceOf
272 SupportClass.Tokenizer sub = new SupportClass.Tokenizer(value_Renamed, "*", true);
273 // SupportClass.Tokenizer sub = new SupportClass.Tokenizer(value_Renamed, "*");//, true);
274 Asn1SequenceOf seq = new Asn1SequenceOf(5);
275 int tokCnt = sub.Count;
278 System.String lastTok = new System.Text.StringBuilder("").ToString();
280 while (sub.HasMoreTokens())
282 System.String subTok = sub.NextToken();
284 if (subTok.Equals("*"))
286 // if previous token was '*', and since the current
287 // token is a '*', we need to insert 'any'
288 if (lastTok.Equals(subTok))
291 seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, ANY), new RfcLdapString(unescapeString("")), false));
296 // value (RfcLdapString)
300 seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, INITIAL), new RfcLdapString(unescapeString(subTok)), false));
302 else if (cnt < tokCnt)
305 seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, ANY), new RfcLdapString(unescapeString(subTok)), false));
310 seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, FINAL), new RfcLdapString(unescapeString(subTok)), false));
316 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, SUBSTRINGS), new RfcSubstringFilter(new RfcAttributeDescription(ft.Attr), seq), false);
321 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, EQUALITY_MATCH), new RfcAttributeValueAssertion(new RfcAttributeDescription(ft.Attr), new RfcAssertionValue(unescapeString(value_Renamed))), false);
325 case EXTENSIBLE_MATCH:
326 System.String type = null, matchingRule = null;
327 bool dnAttributes = false;
328 // SupportClass.Tokenizer st = new StringTokenizer(ft.Attr, ":", true);
329 SupportClass.Tokenizer st = new SupportClass.Tokenizer(ft.Attr, ":");//, true);
332 while (st.HasMoreTokens())
334 System.String s = st.NextToken().Trim();
335 if (first && !s.Equals(":"))
339 // dn must be lower case to be considered dn of the Entry.
340 else if (s.Equals("dn"))
344 else if (!s.Equals(":"))
351 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, EXTENSIBLE_MATCH), new RfcMatchingRuleAssertion(((System.Object) matchingRule == null)?null:new RfcMatchingRuleId(matchingRule), ((System.Object) type == null)?null:new RfcAttributeDescription(type), new RfcAssertionValue(unescapeString(value_Renamed)), (dnAttributes == false)?null:new Asn1Boolean(true)), false);
360 /// <summary> Must have 1 or more Filters</summary>
361 private Asn1SetOf parseFilterList()
363 Asn1SetOf set_Renamed = new Asn1SetOf();
365 set_Renamed.add(parseFilter()); // must have at least 1 filter
367 while (ft.peekChar() == '(')
369 // check for more filters
370 set_Renamed.add(parseFilter());
375 /// <summary> Convert hex character to an integer. Return -1 if char is something
376 /// other than a hex char.
378 internal static int hex2int(char c)
380 return (c >= '0' && c <= '9')?c - '0':(c >= 'A' && c <= 'F')?c - 'A' + 10:(c >= 'a' && c <= 'f')?c - 'a' + 10:- 1;
383 /// <summary> Replace escaped hex digits with the equivalent binary representation.
384 /// Assume either V2 or V3 escape mechanisms:
385 /// V2: \*, \(, \), \\.
386 /// V3: \2A, \28, \29, \5C, \00.
389 /// <param name="string"> A part of the input filter string to be converted.
392 /// <returns> octet-string encoding of the specified string.
394 private sbyte[] unescapeString(System.String string_Renamed)
396 // give octets enough space to grow
397 sbyte[] octets = new sbyte[string_Renamed.Length * 3];
398 // index for string and octets
399 int iString, iOctets;
400 // escape==true means we are in an escape sequence.
402 // escStart==true means we are reading the first character of an escape.
403 bool escStart = false;
405 int ival, length = string_Renamed.Length;
407 char ch; // Character we are adding to the octet string
408 char[] ca = new char[1]; // used while converting multibyte UTF-8 char
409 char temp = (char) (0); // holds the value of the escaped sequence
411 // loop through each character of the string and copy them into octets
412 // converting escaped sequences when needed
413 for (iString = 0, iOctets = 0; iString < length; iString++)
415 ch = string_Renamed[iString];
418 if ((ival = hex2int(ch)) < 0)
420 // Invalid escape value(not a hex character)
421 throw new LdapLocalException(ExceptionMessages.INVALID_ESCAPE, new System.Object[]{ch}, LdapException.FILTER_ERROR);
428 temp = (char) (ival << 4); // high bits of escaped char
433 temp |= (char) (ival); // all bits of escaped char
434 octets[iOctets++] = (sbyte) temp;
435 escStart = escape = false;
441 escStart = escape = true;
447 // place the character into octets.
448 if ((ch >= 0x01 && ch <= 0x27) || (ch >= 0x2B && ch <= 0x5B) || (ch >= 0x5D))
453 // char = %x01-27 / %x2b-5b / %x5d-7f
454 octets[iOctets++] = (sbyte) ch;
458 // char > 0x7f, could be encoded in 2 or 3 bytes
460 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8");
461 byte[] ibytes = encoder.GetBytes(new System.String(ca));
462 utf8Bytes=SupportClass.ToSByteArray(ibytes);
464 // utf8Bytes = new System.String(ca).getBytes("UTF-8");
465 // copy utf8 encoded character into octets
466 Array.Copy((System.Array) SupportClass.ToByteArray(utf8Bytes), 0, (System.Array)SupportClass.ToByteArray( octets), iOctets, utf8Bytes.Length);
467 iOctets = iOctets + utf8Bytes.Length;
473 // found invalid character
474 System.String escString = "";
476 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8");
477 byte[] ibytes = encoder.GetBytes(new System.String(ca));
478 utf8Bytes=SupportClass.ToSByteArray(ibytes);
480 // utf8Bytes = new System.String(ca).getBytes("UTF-8");
481 for (int i = 0; i < utf8Bytes.Length; i++)
483 sbyte u = utf8Bytes[i];
484 if ((u >= 0) && (u < 0x10))
486 escString = escString + "\\0" + System.Convert.ToString(u & 0xff, 16);
490 escString = escString + "\\" + System.Convert.ToString(u & 0xff, 16);
493 throw new LdapLocalException(ExceptionMessages.INVALID_CHAR_IN_FILTER, new System.Object[]{ch, escString}, LdapException.FILTER_ERROR);
496 catch (System.IO.IOException ue)
498 throw new System.SystemException("UTF-8 String encoding not supported by JVM");
503 // Verify that any escape sequence completed
504 if (escStart || escape)
506 throw new LdapLocalException(ExceptionMessages.SHORT_ESCAPE, LdapException.FILTER_ERROR);
509 sbyte[] toReturn = new sbyte[iOctets];
510 // Array.Copy((System.Array)SupportClass.ToByteArray(octets), 0, (System.Array)SupportClass.ToByteArray(toReturn), 0, iOctets);
511 Array.Copy((System.Array)octets, 0, (System.Array)toReturn, 0, iOctets);
517 /* **********************************************************************
518 * The following methods aid in building filters sequentially,
519 * and is used by DSMLHandler:
520 ***********************************************************************/
522 /// <summary> Called by sequential filter building methods to add to a filter
525 /// Verifies that the specified Asn1Object can be added, then adds the
526 /// object to the filter.
528 /// <param name="current"> Filter component to be added to the filter
529 /// @throws LdapLocalException Occurs when an invalid component is added, or
530 /// when the component is out of sequence.
532 private void addObject(Asn1Object current)
534 if (filterStack == null)
536 filterStack = new System.Collections.Stack();
538 if (choiceValue() == null)
540 //ChoiceValue is the root Asn1 node
541 ChoiceValue = current;
545 Asn1Tagged topOfStack = (Asn1Tagged) filterStack.Peek();
546 Asn1Object value_Renamed = topOfStack.taggedValue();
547 if (value_Renamed == null)
549 topOfStack.TaggedValue = current;
550 filterStack.Push(current);
551 // filterStack.Add(current);
553 else if (value_Renamed is Asn1SetOf)
555 ((Asn1SetOf) value_Renamed).add(current);
556 //don't add this to the stack:
558 else if (value_Renamed is Asn1Set)
560 ((Asn1Set) value_Renamed).add(current);
561 //don't add this to the stack:
563 else if (value_Renamed.getIdentifier().Tag == LdapSearchRequest.NOT)
565 throw new LdapLocalException("Attemp to create more than one 'not' sub-filter", LdapException.FILTER_ERROR);
568 int type = current.getIdentifier().Tag;
569 if (type == AND || type == OR || type == NOT)
571 // filterStack.Add(current);
572 filterStack.Push(current);
577 /// <summary> Creates and addes a substrings filter component.
579 /// startSubstrings must be immediatly followed by at least one
580 /// {@link #addSubstring} method and one {@link #endSubstrings} method
581 /// @throws Novell.Directory.Ldap.LdapLocalException
582 /// Occurs when this component is created out of sequence.
584 public virtual void startSubstrings(System.String attrName)
587 Asn1SequenceOf seq = new Asn1SequenceOf(5);
588 Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, SUBSTRINGS), new RfcSubstringFilter(new RfcAttributeDescription(attrName), seq), false);
590 SupportClass.StackPush(filterStack, seq);
594 /// <summary> Adds a Substring component of initial, any or final substring matching.
596 /// This method can be invoked only if startSubString was the last filter-
597 /// building method called. A substring is not required to have an 'INITIAL'
598 /// substring. However, when a filter contains an 'INITIAL' substring only
599 /// one can be added, and it must be the first substring added. Any number of
600 /// 'ANY' substrings can be added. A substring is not required to have a
601 /// 'FINAL' substrings either. However, when a filter does contain a 'FINAL'
602 /// substring only one can be added, and it must be the last substring added.
605 /// <param name="type">Substring type: INITIAL | ANY | FINAL]
607 /// <param name="value">Value to use for matching
608 /// @throws LdapLocalException Occurs if this method is called out of
609 /// sequence or the type added is out of sequence.
611 [CLSCompliantAttribute(false)]
612 public virtual void addSubstring(int type, sbyte[] value_Renamed)
616 Asn1SequenceOf substringSeq = (Asn1SequenceOf) filterStack.Peek();
617 if (type != INITIAL && type != ANY && type != FINAL)
619 throw new LdapLocalException("Attempt to add an invalid " + "substring type", LdapException.FILTER_ERROR);
622 if (type == INITIAL && substringSeq.size() != 0)
624 throw new LdapLocalException("Attempt to add an initial " + "substring match after the first substring", LdapException.FILTER_ERROR);
628 throw new LdapLocalException("Attempt to add a substring " + "match after a final substring match", LdapException.FILTER_ERROR);
634 substringSeq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, type), new RfcLdapString(value_Renamed), false));
636 catch (System.InvalidCastException e)
638 throw new LdapLocalException("A call to addSubstring occured " + "without calling startSubstring", LdapException.FILTER_ERROR);
643 /// <summary> Completes a SubString filter component.
645 /// @throws LdapLocalException Occurs when this is called out of sequence,
646 /// or the substrings filter is empty.
648 public virtual void endSubstrings()
653 Asn1SequenceOf substringSeq = (Asn1SequenceOf) filterStack.Peek();
654 if (substringSeq.size() == 0)
656 throw new LdapLocalException("Empty substring filter", LdapException.FILTER_ERROR);
659 catch (System.InvalidCastException e)
661 throw new LdapLocalException("Missmatched ending of substrings", LdapException.FILTER_ERROR);
667 /// <summary> Creates and adds an AttributeValueAssertion to the filter.
670 /// <param name="rfcType">Filter type: EQUALITY_MATCH | GREATER_OR_EQUAL
671 /// | LESS_OR_EQUAL | APPROX_MATCH ]
673 /// <param name="attrName">Name of the attribute to be asserted
675 /// <param name="value">Value of the attribute to be asserted
676 /// @throws LdapLocalException
677 /// Occurs when the filter type is not a valid attribute assertion.
679 [CLSCompliantAttribute(false)]
680 public virtual void addAttributeValueAssertion(int rfcType, System.String attrName, sbyte[] value_Renamed)
682 if (filterStack != null && !(filterStack.Count == 0) && filterStack.Peek() is Asn1SequenceOf)
684 //If a sequenceof is on the stack then substring is left on the stack
685 throw new LdapLocalException("Cannot insert an attribute assertion in a substring", LdapException.FILTER_ERROR);
687 if ((rfcType != EQUALITY_MATCH) && (rfcType != GREATER_OR_EQUAL) && (rfcType != LESS_OR_EQUAL) && (rfcType != APPROX_MATCH))
689 throw new LdapLocalException("Invalid filter type for AttributeValueAssertion", LdapException.FILTER_ERROR);
691 Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, rfcType), new RfcAttributeValueAssertion(new RfcAttributeDescription(attrName), new RfcAssertionValue(value_Renamed)), false);
696 /// <summary> Creates and adds a present matching to the filter.
699 /// <param name="attrName">Name of the attribute to check for presence.
700 /// @throws LdapLocalException
701 /// Occurs if addPresent is called out of sequence.
703 public virtual void addPresent(System.String attrName)
705 Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, PRESENT), new RfcAttributeDescription(attrName), false);
710 /// <summary> Adds an extensible match to the filter.
713 /// <param name="">matchingRule
714 /// OID or name of the matching rule to use for comparison
716 /// <param name="attrName"> Name of the attribute to match.
718 /// <param name="value"> Value of the attribute to match against.
720 /// <param name="useDNMatching">Indicates whether DN matching should be used.
721 /// @throws LdapLocalException
722 /// Occurs when addExtensibleMatch is called out of sequence.
724 [CLSCompliantAttribute(false)]
725 public virtual void addExtensibleMatch(System.String matchingRule, System.String attrName, sbyte[] value_Renamed, bool useDNMatching)
727 Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, EXTENSIBLE_MATCH), new RfcMatchingRuleAssertion(((System.Object) matchingRule == null)?null:new RfcMatchingRuleId(matchingRule), ((System.Object) attrName == null)?null:new RfcAttributeDescription(attrName), new RfcAssertionValue(value_Renamed), (useDNMatching == false)?null:new Asn1Boolean(true)), false);
732 /// <summary> Creates and adds the Asn1Tagged value for a nestedFilter: AND, OR, or
735 /// Note that a Not nested filter can only have one filter, where AND
739 /// <param name="rfcType">Filter type:
741 /// @throws Novell.Directory.Ldap.LdapLocalException
743 public virtual void startNestedFilter(int rfcType)
746 if (rfcType == AND || rfcType == OR)
748 current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, rfcType), new Asn1SetOf(), false);
750 else if (rfcType == NOT)
752 current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, rfcType), null, true);
756 throw new LdapLocalException("Attempt to create a nested filter other than AND, OR or NOT", LdapException.FILTER_ERROR);
762 /// <summary> Completes a nested filter and checks for the valid filter type.</summary>
763 /// <param name="rfcType"> Type of filter to complete.
764 /// @throws Novell.Directory.Ldap.LdapLocalException Occurs when the specified
765 /// type differs from the current filter component.
767 public virtual void endNestedFilter(int rfcType)
771 //if this is a Not than Not should be the second thing on the stack
774 int topOfStackType = ((Asn1Object) filterStack.Peek()).getIdentifier().Tag;
775 if (topOfStackType != rfcType)
777 throw new LdapLocalException("Missmatched ending of nested filter", LdapException.FILTER_ERROR);
783 /// <summary> Creates an iterator over the preparsed segments of a filter.
785 /// The first object returned by an iterator is an integer indicating the
786 /// type of filter components. Subseqence values are returned. If a
787 /// component is of type 'AND' or 'OR' or 'NOT' then the value
788 /// returned is another iterator. This iterator is used by toString.
791 /// <returns> Iterator over filter segments
793 public virtual System.Collections.IEnumerator getFilterIterator()
795 return new FilterIterator(this, (Asn1Tagged) this.choiceValue());
798 /// <summary> Creates and returns a String representation of this filter.</summary>
799 public virtual System.String filterToString()
801 System.Text.StringBuilder filter = new System.Text.StringBuilder();
802 stringFilter(this.getFilterIterator(), filter);
803 return filter.ToString();
806 /// <summary> Uses a filterIterator to create a string representation of a filter.
809 /// <param name="itr">Iterator of filter components
811 /// <param name="filter">Buffer to place a string representation of the filter
813 /// <seealso cref="FilterIterator">
815 private static void stringFilter(System.Collections.IEnumerator itr, System.Text.StringBuilder filter)
819 while (itr.MoveNext())
821 System.Object filterpart = itr.Current;
822 if (filterpart is System.Int32)
824 op = ((System.Int32) filterpart);
840 case EQUALITY_MATCH: {
841 filter.Append((System.String) itr.Current);
843 sbyte[] value_Renamed = (sbyte[]) itr.Current;
844 filter.Append(byteString(value_Renamed));
848 case GREATER_OR_EQUAL: {
849 filter.Append((System.String) itr.Current);
851 sbyte[] value_Renamed = (sbyte[]) itr.Current;
852 filter.Append(byteString(value_Renamed));
856 case LESS_OR_EQUAL: {
857 filter.Append((System.String) itr.Current);
859 sbyte[] value_Renamed = (sbyte[]) itr.Current;
860 filter.Append(byteString(value_Renamed));
865 filter.Append((System.String) itr.Current);
870 filter.Append((System.String) itr.Current);
872 sbyte[] value_Renamed2 = (sbyte[]) itr.Current;
873 filter.Append(byteString(value_Renamed2));
876 case EXTENSIBLE_MATCH:
877 System.String oid = (System.String) itr.Current;
879 filter.Append((System.String) itr.Current);
883 filter.Append((System.String) itr.Current);
887 filter.Append((System.String) itr.Current);
889 bool noStarLast = false;
890 while (itr.MoveNext())
892 op = ((System.Int32) itr.Current);
897 filter.Append((System.String) itr.Current);
905 filter.Append((System.String) itr.Current);
913 filter.Append((System.String) itr.Current);
921 else if (filterpart is System.Collections.IEnumerator)
923 stringFilter((System.Collections.IEnumerator) filterpart, filter);
929 /// <summary> Convert a UTF8 encoded string, or binary data, into a String encoded for
932 private static System.String byteString(sbyte[] value_Renamed)
934 System.String toReturn = null;
935 if (Novell.Directory.Ldap.Utilclass.Base64.isValidUTF8(value_Renamed, true))
939 System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8");
940 char[] dchar = encoder.GetChars(SupportClass.ToByteArray(value_Renamed));
941 toReturn = new String(dchar);
943 // toReturn = new String(value_Renamed, "UTF-8");
945 catch (System.IO.IOException e)
947 throw new System.SystemException("Default JVM does not support UTF-8 encoding" + e);
952 System.Text.StringBuilder binary = new System.Text.StringBuilder();
953 for (int i = 0; i < value_Renamed.Length; i++)
955 //TODO repair binary output
956 //Every octet needs to be escaped
957 if (value_Renamed[i] >= 0)
959 //one character hex string
960 binary.Append("\\0");
961 binary.Append(System.Convert.ToString(value_Renamed[i], 16));
965 //negative (eight character) hex string
966 binary.Append("\\" + System.Convert.ToString(value_Renamed[i], 16).Substring(6));
969 toReturn = binary.ToString();
974 /// <summary> This inner class wrappers the Search Filter with an iterator.
975 /// This iterator will give access to all the individual components
976 /// preparsed. The first call to next will return an Integer identifying
977 /// the type of filter component. Then the component values will be returned
978 /// AND, NOT, and OR components values will be returned as Iterators.
980 private class FilterIterator : System.Collections.IEnumerator
982 public void Reset(){}
983 private void InitBlock(RfcFilter enclosingInstance)
985 this.enclosingInstance = enclosingInstance;
987 private RfcFilter enclosingInstance;
988 /// <summary> Returns filter identifiers and components of a filter.
990 /// The first object returned is an Integer identifying
993 public virtual System.Object Current
997 System.Object toReturn = null;
1001 toReturn = root.getIdentifier().Tag;
1005 Asn1Object asn1 = root.taggedValue();
1007 if (asn1 is RfcLdapString)
1009 //one value to iterate
1011 toReturn = ((RfcLdapString) asn1).stringValue();
1013 else if (asn1 is RfcSubstringFilter)
1016 RfcSubstringFilter sub = (RfcSubstringFilter) asn1;
1019 //return attribute name
1021 RfcAttributeDescription attr = (RfcAttributeDescription) sub.get_Renamed(0);
1022 toReturn = attr.stringValue();
1024 else if (index % 2 == 0)
1026 //return substring identifier
1027 Asn1SequenceOf substrs = (Asn1SequenceOf) sub.get_Renamed(1);
1028 toReturn = ((Asn1Tagged) substrs.get_Renamed(index / 2)).getIdentifier().Tag;
1033 //return substring value
1034 Asn1SequenceOf substrs = (Asn1SequenceOf) sub.get_Renamed(1);
1035 Asn1Tagged tag = (Asn1Tagged) substrs.get_Renamed(index / 2);
1036 RfcLdapString value_Renamed = (RfcLdapString) tag.taggedValue();
1037 toReturn = value_Renamed.stringValue();
1040 if (index / 2 >= ((Asn1SequenceOf) sub.get_Renamed(1)).size())
1045 else if (asn1 is RfcAttributeValueAssertion)
1047 // components: =,>=,<=,~=
1048 RfcAttributeValueAssertion assertion = (RfcAttributeValueAssertion) asn1;
1052 toReturn = assertion.AttributeDescription;
1055 else if (index == 1)
1057 toReturn = assertion.AssertionValue;
1062 else if (asn1 is RfcMatchingRuleAssertion)
1065 RfcMatchingRuleAssertion exMatch = (RfcMatchingRuleAssertion) asn1;
1070 toReturn = ((Asn1OctetString) ((Asn1Tagged) exMatch.get_Renamed(index++)).taggedValue()).stringValue();
1076 else if (asn1 is Asn1SetOf)
1078 //AND and OR nested components
1079 Asn1SetOf set_Renamed = (Asn1SetOf) asn1;
1084 toReturn = new FilterIterator(enclosingInstance,(Asn1Tagged) set_Renamed.get_Renamed(index++));
1085 if (index >= set_Renamed.size())
1087 this.hasMore = false;
1090 else if (asn1 is Asn1Tagged)
1092 //NOT nested component.
1093 toReturn = new FilterIterator(enclosingInstance,(Asn1Tagged) asn1);
1094 this.hasMore = false;
1101 public RfcFilter Enclosing_Instance
1105 return enclosingInstance;
1109 internal Asn1Tagged root;
1110 /// <summary>indicates if the identifier for a component has been returned yet </summary>
1111 internal bool tagReturned = false;
1112 /// <summary>indexes the several parts a component may have </summary>
1113 internal int index = - 1;
1114 private bool hasMore = true;
1116 public FilterIterator(RfcFilter enclosingInstance, Asn1Tagged root)
1118 InitBlock(enclosingInstance);
1121 public virtual bool MoveNext()
1126 public void remove()
1128 throw new System.NotSupportedException("Remove is not supported on a filter iterator");
1132 /// <summary> This inner class will tokenize the components of an RFC 2254 search filter.</summary>
1133 internal class FilterTokenizer
1135 private void InitBlock(RfcFilter enclosingInstance)
1137 this.enclosingInstance = enclosingInstance;
1139 private RfcFilter enclosingInstance;
1140 /// <summary> Reads either an operator, or an attribute, whichever is
1141 /// next in the filter string.
1144 /// If the next component is an attribute, it is read and stored in the
1145 /// attr field of this class which may be retrieved with getAttr()
1146 /// and a -1 is returned. Otherwise, the int value of the operator read is
1149 virtual public int OpOrAttr
1155 if (offset >= filterLength)
1157 //"Unexpected end of filter",
1158 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1161 int testChar = filter[offset];
1162 if (testChar == '&')
1165 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.AND;
1167 else if (testChar == '|')
1170 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.OR;
1172 else if (testChar == '!')
1175 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.NOT;
1179 if (filter.Substring(offset).StartsWith(":=") == true)
1181 throw new LdapLocalException(ExceptionMessages.NO_MATCHING_RULE, LdapException.FILTER_ERROR);
1184 if (filter.Substring(offset).StartsWith("::=") == true || filter.Substring(offset).StartsWith(":::=") == true)
1186 throw new LdapLocalException(ExceptionMessages.NO_DN_NOR_MATCHING_RULE, LdapException.FILTER_ERROR);
1190 // get first component of 'item' (attr or :dn or :matchingrule)
1191 System.String delims = "=~<>()";
1192 System.Text.StringBuilder sb = new System.Text.StringBuilder();
1194 while (delims.IndexOf((System.Char) filter[offset]) == - 1 && filter.Substring(offset).StartsWith(":=") == false)
1196 sb.Append(filter[offset++]);
1199 attr = sb.ToString().Trim();
1201 // is there an attribute name specified in the filter ?
1202 if (attr.Length == 0 || attr[0] == ';')
1204 throw new LdapLocalException(ExceptionMessages.NO_ATTRIBUTE_NAME, LdapException.FILTER_ERROR);
1207 for (index = 0; index < attr.Length; index++)
1209 char atIndex = attr[index];
1210 if (!(System.Char.IsLetterOrDigit(atIndex) || atIndex == '-' || atIndex == '.' || atIndex == ';' || atIndex == ':'))
1213 if (atIndex == '\\')
1215 throw new LdapLocalException(ExceptionMessages.INVALID_ESC_IN_DESCR, LdapException.FILTER_ERROR);
1219 throw new LdapLocalException(ExceptionMessages.INVALID_CHAR_IN_DESCR, new System.Object[]{atIndex}, LdapException.FILTER_ERROR);
1224 // is there an option specified in the filter ?
1225 index = attr.IndexOf((System.Char) ';');
1226 if (index != - 1 && index == attr.Length - 1)
1228 throw new LdapLocalException(ExceptionMessages.NO_OPTION, LdapException.FILTER_ERROR);
1236 /// <summary> Reads an RFC 2251 filter type from the filter string and returns its
1239 virtual public int FilterType
1243 if (offset >= filterLength)
1245 //"Unexpected end of filter",
1246 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1249 if (filter.Substring(offset).StartsWith(">="))
1252 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.GREATER_OR_EQUAL;
1254 else if (filter.Substring(offset).StartsWith("<="))
1257 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.LESS_OR_EQUAL;
1259 else if (filter.Substring(offset).StartsWith("~="))
1262 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.APPROX_MATCH;
1264 else if (filter.Substring(offset).StartsWith(":="))
1267 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.EXTENSIBLE_MATCH;
1269 else if (filter[offset] == '=')
1272 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.EQUALITY_MATCH;
1276 //"Invalid comparison operator",
1277 throw new LdapLocalException(ExceptionMessages.INVALID_FILTER_COMPARISON, LdapException.FILTER_ERROR);
1283 /// <summary> Reads a value from a filter string.</summary>
1284 virtual public System.String Value
1288 if (offset >= filterLength)
1290 //"Unexpected end of filter",
1291 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1294 int idx = filter.IndexOf((System.Char) ')', offset);
1299 System.String ret = filter.Substring(offset, (idx) - (offset));
1306 /// <summary> Returns the current attribute identifier.</summary>
1307 virtual public System.String Attr
1315 public RfcFilter Enclosing_Instance
1319 return enclosingInstance;
1324 //*************************************************************************
1325 // Private variables
1326 //*************************************************************************
1328 private System.String filter; // The filter string to parse
1329 private System.String attr; // Name of the attribute just parsed
1330 private int offset; // Offset pointer into the filter string
1331 private int filterLength; // Length of the filter string to parse
1333 //*************************************************************************
1335 //*************************************************************************
1337 /// <summary> Constructs a FilterTokenizer for a filter.</summary>
1338 public FilterTokenizer(RfcFilter enclosingInstance, System.String filter)
1340 InitBlock(enclosingInstance);
1341 this.filter = filter;
1343 this.filterLength = filter.Length;
1347 //*************************************************************************
1348 // Tokenizer methods
1349 //*************************************************************************
1351 /// <summary> Reads the current char and throws an Exception if it is not a left
1354 public void getLeftParen()
1356 if (offset >= filterLength)
1358 //"Unexpected end of filter",
1359 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1361 if (filter[offset++] != '(')
1363 //"Missing left paren",
1364 throw new LdapLocalException(ExceptionMessages.EXPECTING_LEFT_PAREN, new System.Object[]{filter[offset -= 1]}, LdapException.FILTER_ERROR);
1369 /// <summary> Reads the current char and throws an Exception if it is not a right
1372 public void getRightParen()
1374 if (offset >= filterLength)
1376 //"Unexpected end of filter",
1377 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1379 if (filter[offset++] != ')')
1381 //"Missing right paren",
1382 throw new LdapLocalException(ExceptionMessages.EXPECTING_RIGHT_PAREN, new System.Object[]{filter[offset - 1]}, LdapException.FILTER_ERROR);
1387 /// <summary> Return the current char without advancing the offset pointer. This is
1388 /// used by ParseFilterList when determining if there are any more
1389 /// Filters in the list.
1391 public char peekChar()
1393 if (offset >= filterLength)
1395 //"Unexpected end of filter",
1396 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1398 return filter[offset];