Merge pull request #487 from mayerwin/patch-1
[mono.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap.Rfc2251 / RfcFilter.cs
1 /******************************************************************************
2 * The MIT License
3 * Copyright (c) 2003 Novell Inc.  www.novell.com
4
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:
11
12 * The above copyright notice and this permission notice shall be included in 
13 * all copies or substantial portions of the Software.
14
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
21 * SOFTWARE.
22 *******************************************************************************/
23 //
24 // Novell.Directory.Ldap.Rfc2251.RfcFilter.cs
25 //
26 // Author:
27 //   Sunil Kumar (Sunilk@novell.com)
28 //
29 // (C) 2003 Novell, Inc (http://www.novell.com)
30 //
31
32 using System;
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;
38
39 namespace Novell.Directory.Ldap.Rfc2251
40 {
41         
42         /// <summary> Represents an Ldap Filter.
43         /// 
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.
47         /// 
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}.
52         /// 
53         /// More filters can be nested together into more complex filters with the
54         /// following filter components: {@link #AND}, {@link #OR}, {@link #NOT} 
55         /// 
56         /// Substrings can have three components:
57         /// <pre>
58         /// Filter ::= CHOICE {
59         /// and             [0] SET OF Filter,
60         /// or              [1] SET OF Filter,
61         /// not             [2] 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 }
69         /// </pre>
70         /// </summary>
71         public class RfcFilter:Asn1Choice
72         {
73                 //*************************************************************************
74                 // Public variables for Filter
75                 //*************************************************************************
76                 
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;
97                 
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;
104                 
105                 //*************************************************************************
106                 // Private variables for Filter
107                 //*************************************************************************
108                 
109                 private FilterTokenizer ft;
110                 private System.Collections.Stack filterStack;
111                 private bool finalFound;
112                 
113                 //*************************************************************************
114                 // Constructor for Filter
115                 //*************************************************************************
116                 
117                 /// <summary> Constructs a Filter object by parsing an RFC 2254 Search Filter String.</summary>
118                 public RfcFilter(System.String filter):base(null)
119                 {
120                         ChoiceValue = parse(filter);
121                         return ;
122                 }
123                 
124                 /// <summary> Constructs a Filter object that will be built up piece by piece.   </summary>
125                 public RfcFilter():base(null)
126                 {
127                         filterStack = new System.Collections.Stack();
128                         //The choice value must be set later: setChoiceValue(rootFilterTag)
129                         return ;
130                 }
131                 
132                 //*************************************************************************
133                 // Helper methods for RFC 2254 Search Filter parsing.
134                 //*************************************************************************
135                 
136                 /// <summary> Parses an RFC 2251 filter string into an ASN.1 Ldap Filter object.</summary>
137                 private Asn1Tagged parse(System.String filterExpr)
138                 {
139
140                         if ((System.Object) filterExpr == null || filterExpr.Equals(""))
141                         {
142                                 filterExpr = new System.Text.StringBuilder("(objectclass=*)").ToString();
143                         }
144                         int idx;
145                         if ((idx = filterExpr.IndexOf((System.Char) '\\')) != - 1)
146                         {
147                                 System.Text.StringBuilder sb = new System.Text.StringBuilder(filterExpr);
148                                 int i = idx;
149                                 while (i < (sb.Length - 1))
150                                 {
151                                         char c = sb[i++];
152                                         if (c == '\\')
153                                         {
154                                                 // found '\' (backslash)
155                                                 // If V2 escape, turn to a V3 escape
156                                                 c = sb[i];
157                                                 if (c == '*' || c == '(' || c == ')' || c == '\\')
158                                                 {
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));
162                                                         i += 2;
163                                                 }
164                                         }
165                                 }
166                                 filterExpr = sb.ToString();
167                         }
168                         
169                         // missing opening and closing parentheses, must be V2, add parentheses
170                         if ((filterExpr[0] != '(') && (filterExpr[filterExpr.Length - 1] != ')'))
171                         {
172                                 filterExpr = "(" + filterExpr + ")";
173                         }
174                         
175                         char ch = filterExpr[0];
176                         int len = filterExpr.Length;
177                         
178                         // missing opening parenthesis ?
179                         if (ch != '(')
180                         {
181                                 throw new LdapLocalException(ExceptionMessages.MISSING_LEFT_PAREN, LdapException.FILTER_ERROR);
182                         }
183                         
184                         // missing closing parenthesis ?
185                         if (filterExpr[len - 1] != ')')
186                         {
187                                 throw new LdapLocalException(ExceptionMessages.MISSING_RIGHT_PAREN, LdapException.FILTER_ERROR);
188                         }
189                         
190                         // unmatched parentheses ?
191                         int parenCount = 0;
192                         for (int i = 0; i < len; i++)
193                         {
194                                 if (filterExpr[i] == '(')
195                                 {
196                                         parenCount++;
197                                 }
198                                 
199                                 if (filterExpr[i] == ')')
200                                 {
201                                         parenCount--;
202                                 }
203                         }
204                         
205                         if (parenCount > 0)
206                         {
207                                 throw new LdapLocalException(ExceptionMessages.MISSING_RIGHT_PAREN, LdapException.FILTER_ERROR);
208                         }
209                         
210                         if (parenCount < 0)
211                         {
212                                 throw new LdapLocalException(ExceptionMessages.MISSING_LEFT_PAREN, LdapException.FILTER_ERROR);
213                         }
214                         ft = new FilterTokenizer(this, filterExpr);
215                         
216                         return parseFilter();
217                 }
218                 
219                 /// <summary> Parses an RFC 2254 filter</summary>
220                 private Asn1Tagged parseFilter()
221                 {
222                         ft.getLeftParen();
223                         
224                         Asn1Tagged filter = parseFilterComp();
225                         
226                         ft.getRightParen();
227                         
228                         return filter;
229                 }
230                 
231                 /// <summary> RFC 2254 filter helper method. Will Parse a filter component.</summary>
232                 private Asn1Tagged parseFilterComp()
233                 {
234                         Asn1Tagged tag = null;
235                         int filterComp = ft.OpOrAttr;
236                         
237                         switch (filterComp)
238                         {
239                                 
240                                 case AND: 
241                                 case OR: 
242                                         tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, filterComp), parseFilterList(), false);
243                                         break;
244                                 
245                                 case NOT: 
246                                         tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, filterComp), parseFilter(), true);
247                                         break;
248                                 
249                                 default: 
250                                         int filterType = ft.FilterType;
251                                         System.String value_Renamed = ft.Value;
252                                         
253                                         switch (filterType)
254                                         {
255                                                 
256                                                 case GREATER_OR_EQUAL: 
257                                                 case LESS_OR_EQUAL: 
258                                                 case APPROX_MATCH: 
259                                                         tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, filterType), new RfcAttributeValueAssertion(new RfcAttributeDescription(ft.Attr), new RfcAssertionValue(unescapeString(value_Renamed))), false);
260                                                         break;
261                                                 
262                                                 case EQUALITY_MATCH: 
263                                                         if (value_Renamed.Equals("*"))
264                                                         {
265                                                                 // present
266                                                                 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, PRESENT), new RfcAttributeDescription(ft.Attr), false);
267                                                         }
268                                                         else if (value_Renamed.IndexOf((System.Char) '*') != - 1)
269                                                         {
270                                                                 // substrings parse:
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;
276                                                                 int cnt = 0;
277                                                                 
278                                                                 System.String lastTok = new System.Text.StringBuilder("").ToString();
279                                                                 
280                                                                 while (sub.HasMoreTokens())
281                                                                 {
282                                                                         System.String subTok = sub.NextToken();
283                                                                         cnt++;
284                                                                         if (subTok.Equals("*"))
285                                                                         {
286                                                                                 // if previous token was '*', and since the current
287                                                                                 // token is a '*', we need to insert 'any'
288                                                                                 if (lastTok.Equals(subTok))
289                                                                                 {
290                                                                                         // '**'
291                                                                                         seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, ANY), new RfcLdapString(unescapeString("")), false));
292                                                                                 }
293                                                                         }
294                                                                         else
295                                                                         {
296                                                                                 // value (RfcLdapString)
297                                                                                 if (cnt == 1)
298                                                                                 {
299                                                                                         // initial
300                                                                                         seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, INITIAL), new RfcLdapString(unescapeString(subTok)), false));
301                                                                                 }
302                                                                                 else if (cnt < tokCnt)
303                                                                                 {
304                                                                                         // any
305                                                                                         seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, ANY), new RfcLdapString(unescapeString(subTok)), false));
306                                                                                 }
307                                                                                 else
308                                                                                 {
309                                                                                         // final
310                                                                                         seq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, FINAL), new RfcLdapString(unescapeString(subTok)), false));
311                                                                                 }
312                                                                         }
313                                                                         lastTok = subTok;
314                                                                 }
315                                                                 
316                                                                 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, SUBSTRINGS), new RfcSubstringFilter(new RfcAttributeDescription(ft.Attr), seq), false);
317                                                         }
318                                                         else
319                                                         {
320                                                                 // simple
321                                                                 tag = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, EQUALITY_MATCH), new RfcAttributeValueAssertion(new RfcAttributeDescription(ft.Attr), new RfcAssertionValue(unescapeString(value_Renamed))), false);
322                                                         }
323                                                         break;
324                                                 
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);
330                                                         
331                                                         bool first = true;
332                                                         while (st.HasMoreTokens())
333                                                         {
334                                                                 System.String s = st.NextToken().Trim();
335                                                                 if (first && !s.Equals(":"))
336                                                                 {
337                                                                         type = s;
338                                                                 }
339                                                                 // dn must be lower case to be considered dn of the Entry.
340                                                                 else if (s.Equals("dn"))
341                                                                 {
342                                                                         dnAttributes = true;
343                                                                 }
344                                                                 else if (!s.Equals(":"))
345                                                                 {
346                                                                         matchingRule = s;
347                                                                 }
348                                                                 first = false;
349                                                         }
350                                                         
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);
352                                                         break;
353                                                 }
354                                         break;
355                                 
356                         }
357                         return tag;
358                 }
359                 
360                 /// <summary> Must have 1 or more Filters</summary>
361                 private Asn1SetOf parseFilterList()
362                 {
363                         Asn1SetOf set_Renamed = new Asn1SetOf();
364                         
365                         set_Renamed.add(parseFilter()); // must have at least 1 filter
366                         
367                         while (ft.peekChar() == '(')
368                         {
369                                 // check for more filters
370                                 set_Renamed.add(parseFilter());
371                         }
372                         return set_Renamed;
373                 }
374                 
375                 /// <summary> Convert hex character to an integer. Return -1 if char is something
376                 /// other than a hex char.
377                 /// </summary>
378                 internal static int hex2int(char c)
379                 {
380                         return (c >= '0' && c <= '9')?c - '0':(c >= 'A' && c <= 'F')?c - 'A' + 10:(c >= 'a' && c <= 'f')?c - 'a' + 10:- 1;
381                 }
382                 
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.
387                 /// 
388                 /// </summary>
389                 /// <param name="string">   A part of the input filter string to be converted.
390                 /// 
391                 /// </param>
392                 /// <returns> octet-string encoding of the specified string.
393                 /// </returns>
394                 private sbyte[] unescapeString(System.String string_Renamed)
395                 {
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.
401                         bool escape = false;
402                         // escStart==true means we are reading the first character of an escape.
403                         bool escStart = false;
404                         
405                         int ival, length = string_Renamed.Length;
406                         sbyte[] utf8Bytes;
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
410                         
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++)
414                         {
415                                 ch = string_Renamed[iString];
416                                 if (escape)
417                                 {
418                                         if ((ival = hex2int(ch)) < 0)
419                                         {
420                                                 // Invalid escape value(not a hex character)
421                                                 throw new LdapLocalException(ExceptionMessages.INVALID_ESCAPE, new System.Object[]{ch}, LdapException.FILTER_ERROR);
422                                         }
423                                         else
424                                         {
425                                                 // V3 escaped: \\**
426                                                 if (escStart)
427                                                 {
428                                                         temp = (char) (ival << 4); // high bits of escaped char
429                                                         escStart = false;
430                                                 }
431                                                 else
432                                                 {
433                                                         temp |= (char) (ival); // all bits of escaped char
434                                                         octets[iOctets++] = (sbyte) temp;
435                                                         escStart = escape = false;
436                                                 }
437                                         }
438                                 }
439                                 else if (ch == '\\')
440                                 {
441                                         escStart = escape = true;
442                                 }
443                                 else
444                                 {
445                                         try
446                                         {
447                                                 // place the character into octets.
448                                                 if ((ch >= 0x01 && ch <= 0x27) || (ch >= 0x2B && ch <= 0x5B) || (ch >= 0x5D))
449                                                 {
450                                                         // found valid char
451                                                         if (ch <= 0x7f)
452                                                         {
453                                                                 // char = %x01-27 / %x2b-5b / %x5d-7f
454                                                                 octets[iOctets++] = (sbyte) ch;
455                                                         }
456                                                         else
457                                                         {
458                                                                 // char > 0x7f, could be encoded in 2 or 3 bytes
459                                                                 ca[0] = ch;
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);
463
464 //                                                              utf8Bytes = new System.String(ca).getBytes("UTF-8");
465                                                                 // copy utf8 encoded character into octets
466                                                                 Array.Copy((System.Array) (utf8Bytes), 0, (System.Array) octets, iOctets, utf8Bytes.Length);
467                                                                 iOctets = iOctets + utf8Bytes.Length;
468                                                         }
469                                                         escape = false;
470                                                 }
471                                                 else
472                                                 {
473                                                         // found invalid character
474                                                         System.String escString = "";
475                                                         ca[0] = ch;
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);
479
480 //                                                      utf8Bytes = new System.String(ca).getBytes("UTF-8");
481                                                         for (int i = 0; i < utf8Bytes.Length; i++)
482                                                         {
483                                                                 sbyte u = utf8Bytes[i];
484                                                                 if ((u >= 0) && (u < 0x10))
485                                                                 {
486                                                                         escString = escString + "\\0" + System.Convert.ToString(u & 0xff, 16);
487                                                                 }
488                                                                 else
489                                                                 {
490                                                                         escString = escString + "\\" + System.Convert.ToString(u & 0xff, 16);
491                                                                 }
492                                                         }
493                                                         throw new LdapLocalException(ExceptionMessages.INVALID_CHAR_IN_FILTER, new System.Object[]{ch, escString}, LdapException.FILTER_ERROR);
494                                                 }
495                                         }
496                                         catch (System.IO.IOException ue)
497                                         {
498                                                 throw new System.SystemException("UTF-8 String encoding not supported by JVM");
499                                         }
500                                 }
501                         }
502                         
503                         // Verify that any escape sequence completed
504                         if (escStart || escape)
505                         {
506                                 throw new LdapLocalException(ExceptionMessages.SHORT_ESCAPE, LdapException.FILTER_ERROR);
507                         }
508                         
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);
512
513                         octets = null;
514                         return toReturn;
515                 }
516                 
517                 /* **********************************************************************
518                 *  The following methods aid in building filters sequentially,
519                 *  and is used by DSMLHandler:
520                 ***********************************************************************/
521                 
522                 /// <summary> Called by sequential filter building methods to add to a filter
523                 /// component.
524                 /// 
525                 /// Verifies that the specified Asn1Object can be added, then adds the
526                 /// object to the filter.
527                 /// </summary>
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.
531                 /// </param>
532                 private void  addObject(Asn1Object current)
533                 {
534                         if (filterStack == null)
535                         {
536                                 filterStack = new System.Collections.Stack();
537                         }
538                         if (choiceValue() == null)
539                         {
540                                 //ChoiceValue is the root Asn1 node
541                                 ChoiceValue = current;
542                         }
543                         else
544                         {
545                                 Asn1Tagged topOfStack = (Asn1Tagged) filterStack.Peek();
546                                 Asn1Object value_Renamed = topOfStack.taggedValue();
547                                 if (value_Renamed == null)
548                                 {
549                                         topOfStack.TaggedValue = current;
550                                         filterStack.Push(current);
551 //                                      filterStack.Add(current);
552                                 }
553                                 else if (value_Renamed is Asn1SetOf)
554                                 {
555                                         ((Asn1SetOf) value_Renamed).add(current);
556                                         //don't add this to the stack:
557                                 }
558                                 else if (value_Renamed is Asn1Set)
559                                 {
560                                         ((Asn1Set) value_Renamed).add(current);
561                                         //don't add this to the stack:
562                                 }
563                                 else if (value_Renamed.getIdentifier().Tag == LdapSearchRequest.NOT)
564                                 {
565                                         throw new LdapLocalException("Attemp to create more than one 'not' sub-filter", LdapException.FILTER_ERROR);
566                                 }
567                         }
568                         int type = current.getIdentifier().Tag;
569                         if (type == AND || type == OR || type == NOT)
570                         {
571 //                              filterStack.Add(current);
572                                 filterStack.Push(current);
573                         }
574                         return ;
575                 }
576                 
577                 /// <summary> Creates and addes a substrings filter component.
578                 /// 
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.
583                 /// </summary>
584                 public virtual void  startSubstrings(System.String attrName)
585                 {
586                         finalFound = false;
587                         Asn1SequenceOf seq = new Asn1SequenceOf(5);
588                         Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, SUBSTRINGS), new RfcSubstringFilter(new RfcAttributeDescription(attrName), seq), false);
589                         addObject(current);
590                         SupportClass.StackPush(filterStack, seq);
591                         return ;
592                 }
593                 
594                 /// <summary> Adds a Substring component of initial, any or final substring matching.
595                 /// 
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.
603                 /// 
604                 /// </summary>
605                 /// <param name="type">Substring type: INITIAL | ANY | FINAL]
606                 /// </param>
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.
610                 /// </param>
611                 [CLSCompliantAttribute(false)]
612                 public virtual void  addSubstring(int type, sbyte[] value_Renamed)
613                 {
614                         try
615                         {
616                                 Asn1SequenceOf substringSeq = (Asn1SequenceOf) filterStack.Peek();
617                                 if (type != INITIAL && type != ANY && type != FINAL)
618                                 {
619                                         throw new LdapLocalException("Attempt to add an invalid " + "substring type", LdapException.FILTER_ERROR);
620                                 }
621                                 
622                                 if (type == INITIAL && substringSeq.size() != 0)
623                                 {
624                                         throw new LdapLocalException("Attempt to add an initial " + "substring match after the first substring", LdapException.FILTER_ERROR);
625                                 }
626                                 if (finalFound)
627                                 {
628                                         throw new LdapLocalException("Attempt to add a substring " + "match after a final substring match", LdapException.FILTER_ERROR);
629                                 }
630                                 if (type == FINAL)
631                                 {
632                                         finalFound = true;
633                                 }
634                                 substringSeq.add(new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, type), new RfcLdapString(value_Renamed), false));
635                         }
636                         catch (System.InvalidCastException e)
637                         {
638                                 throw new LdapLocalException("A call to addSubstring occured " + "without calling startSubstring", LdapException.FILTER_ERROR);
639                         }
640                         return ;
641                 }
642                 
643                 /// <summary> Completes a SubString filter component.
644                 /// 
645                 /// @throws LdapLocalException Occurs when this is called out of sequence,
646                 /// or the substrings filter is empty.
647                 /// </summary>
648                 public virtual void  endSubstrings()
649                 {
650                         try
651                         {
652                                 finalFound = false;
653                                 Asn1SequenceOf substringSeq = (Asn1SequenceOf) filterStack.Peek();
654                                 if (substringSeq.size() == 0)
655                                 {
656                                         throw new LdapLocalException("Empty substring filter", LdapException.FILTER_ERROR);
657                                 }
658                         }
659                         catch (System.InvalidCastException e)
660                         {
661                                 throw new LdapLocalException("Missmatched ending of substrings", LdapException.FILTER_ERROR);
662                         }
663                         filterStack.Pop();
664                         return ;
665                 }
666                 
667                 /// <summary> Creates and adds an AttributeValueAssertion to the filter.
668                 /// 
669                 /// </summary>
670                 /// <param name="rfcType">Filter type: EQUALITY_MATCH | GREATER_OR_EQUAL
671                 /// | LESS_OR_EQUAL | APPROX_MATCH ]
672                 /// </param>
673                 /// <param name="attrName">Name of the attribute to be asserted
674                 /// </param>
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.
678                 /// </param>
679                 [CLSCompliantAttribute(false)]
680                 public virtual void  addAttributeValueAssertion(int rfcType, System.String attrName, sbyte[] value_Renamed)
681                 {
682                         if (filterStack != null && !(filterStack.Count == 0) && filterStack.Peek() is Asn1SequenceOf)
683                         {
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);
686                         }
687                         if ((rfcType != EQUALITY_MATCH) && (rfcType != GREATER_OR_EQUAL) && (rfcType != LESS_OR_EQUAL) && (rfcType != APPROX_MATCH))
688                         {
689                                 throw new LdapLocalException("Invalid filter type for AttributeValueAssertion", LdapException.FILTER_ERROR);
690                         }
691                         Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, rfcType), new RfcAttributeValueAssertion(new RfcAttributeDescription(attrName), new RfcAssertionValue(value_Renamed)), false);
692                         addObject(current);
693                         return ;
694                 }
695                 
696                 /// <summary> Creates and adds a present matching to the filter.
697                 /// 
698                 /// </summary>
699                 /// <param name="attrName">Name of the attribute to check for presence.
700                 /// @throws LdapLocalException
701                 /// Occurs if addPresent is called out of sequence.
702                 /// </param>
703                 public virtual void  addPresent(System.String attrName)
704                 {
705                         Asn1Object current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, false, PRESENT), new RfcAttributeDescription(attrName), false);
706                         addObject(current);
707                         return ;
708                 }
709                 
710                 /// <summary> Adds an extensible match to the filter.
711                 /// 
712                 /// </summary>
713                 /// <param name="">matchingRule
714                 /// OID or name of the matching rule to use for comparison
715                 /// </param>
716                 /// <param name="attrName"> Name of the attribute to match.
717                 /// </param>
718                 /// <param name="value"> Value of the attribute to match against.
719                 /// </param>
720                 /// <param name="useDNMatching">Indicates whether DN matching should be used.
721                 /// @throws LdapLocalException
722                 /// Occurs when addExtensibleMatch is called out of sequence.
723                 /// </param>
724                 [CLSCompliantAttribute(false)]
725                 public virtual void  addExtensibleMatch(System.String matchingRule, System.String attrName, sbyte[] value_Renamed, bool useDNMatching)
726                 {
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);
728                         addObject(current);
729                         return ;
730                 }
731                 
732                 /// <summary> Creates and adds the Asn1Tagged value for a nestedFilter: AND, OR, or
733                 /// NOT.
734                 /// 
735                 /// Note that a Not nested filter can only have one filter, where AND
736                 /// and OR do not
737                 /// 
738                 /// </summary>
739                 /// <param name="rfcType">Filter type:
740                 /// [AND | OR | NOT]
741                 /// @throws Novell.Directory.Ldap.LdapLocalException
742                 /// </param>
743                 public virtual void  startNestedFilter(int rfcType)
744                 {
745                         Asn1Object current;
746                         if (rfcType == AND || rfcType == OR)
747                         {
748                                 current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, rfcType), new Asn1SetOf(), false);
749                         }
750                         else if (rfcType == NOT)
751                         {
752                                 current = new Asn1Tagged(new Asn1Identifier(Asn1Identifier.CONTEXT, true, rfcType), null, true);
753                         }
754                         else
755                         {
756                                 throw new LdapLocalException("Attempt to create a nested filter other than AND, OR or NOT", LdapException.FILTER_ERROR);
757                         }
758                         addObject(current);
759                         return ;
760                 }
761                 
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.
766                 /// </param>
767                 public virtual void  endNestedFilter(int rfcType)
768                 {
769                         if (rfcType == NOT)
770                         {
771                                 //if this is a Not than Not should be the second thing on the stack
772                                 filterStack.Pop();
773                         }
774                         int topOfStackType = ((Asn1Object) filterStack.Peek()).getIdentifier().Tag;
775                         if (topOfStackType != rfcType)
776                         {
777                                 throw new LdapLocalException("Missmatched ending of nested filter", LdapException.FILTER_ERROR);
778                         }
779                         filterStack.Pop();
780                         return ;
781                 }
782                 
783                 /// <summary> Creates an iterator over the preparsed segments of a filter.
784                 /// 
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.
789                 /// 
790                 /// </summary>
791                 /// <returns> Iterator over filter segments
792                 /// </returns>
793                 public virtual System.Collections.IEnumerator getFilterIterator()
794                 {
795                         return new FilterIterator(this, (Asn1Tagged) this.choiceValue());
796                 }
797                 
798                 /// <summary> Creates and returns a String representation of this filter.</summary>
799                 public virtual System.String filterToString()
800                 {
801                         System.Text.StringBuilder filter = new System.Text.StringBuilder();
802                         stringFilter(this.getFilterIterator(), filter);
803                         return filter.ToString();
804                 }
805                 
806                 /// <summary> Uses a filterIterator to create a string representation of a filter.
807                 /// 
808                 /// </summary>
809                 /// <param name="itr">Iterator of filter components
810                 /// </param>
811                 /// <param name="filter">Buffer to place a string representation of the filter
812                 /// </param>
813                 /// <seealso cref="FilterIterator">
814                 /// </seealso>
815                 private static void  stringFilter(System.Collections.IEnumerator itr, System.Text.StringBuilder filter)
816                 {
817                         int op = - 1;
818                         filter.Append('(');
819                         while (itr.MoveNext())
820                         {
821                                 System.Object filterpart = itr.Current;
822                                 if (filterpart is System.Int32)
823                                 {
824                                         op = ((System.Int32) filterpart);
825                                         switch (op)
826                                         {
827                                                 
828                                                 case AND: 
829                                                         filter.Append('&');
830                                                         break;
831                                                 
832                                                 case OR: 
833                                                         filter.Append('|');
834                                                         break;
835                                                 
836                                                 case NOT: 
837                                                         filter.Append('!');
838                                                         break;
839                                                 
840                                                 case EQUALITY_MATCH:  {
841                                                                 filter.Append((System.String) itr.Current);
842                                                                 filter.Append('=');
843                                                                 sbyte[] value_Renamed = (sbyte[]) itr.Current;
844                                                                 filter.Append(byteString(value_Renamed));
845                                                                 break;
846                                                         }
847                                                 
848                                                 case GREATER_OR_EQUAL:  {
849                                                                 filter.Append((System.String) itr.Current);
850                                                                 filter.Append(">=");
851                                                                 sbyte[] value_Renamed = (sbyte[]) itr.Current;
852                                                                 filter.Append(byteString(value_Renamed));
853                                                                 break;
854                                                         }
855                                                 
856                                                 case LESS_OR_EQUAL:  {
857                                                                 filter.Append((System.String) itr.Current);
858                                                                 filter.Append("<=");
859                                                                 sbyte[] value_Renamed = (sbyte[]) itr.Current;
860                                                                 filter.Append(byteString(value_Renamed));
861                                                                 break;
862                                                         }
863                                                 
864                                                 case PRESENT: 
865                                                         filter.Append((System.String) itr.Current);
866                                                         filter.Append("=*");
867                                                         break;
868                                                 
869                                                 case APPROX_MATCH: 
870                                                         filter.Append((System.String) itr.Current);
871                                                         filter.Append("~=");
872                                                         sbyte[] value_Renamed2 = (sbyte[]) itr.Current;
873                                                         filter.Append(byteString(value_Renamed2));
874                                                         break;
875                                                 
876                                                 case EXTENSIBLE_MATCH: 
877                                                         System.String oid = (System.String) itr.Current;
878                                                         
879                                                         filter.Append((System.String) itr.Current);
880                                                         filter.Append(':');
881                                                         filter.Append(oid);
882                                                         filter.Append(":=");
883                                                         filter.Append((System.String) itr.Current);
884                                                         break;
885                                                 
886                                                 case SUBSTRINGS:  {
887                                                                 filter.Append((System.String) itr.Current);
888                                                                 filter.Append('=');
889                                                                 bool noStarLast = false;
890                                                                 while (itr.MoveNext())
891                                                                 {
892                                                                         op = ((System.Int32) itr.Current);
893                                                                         switch (op)
894                                                                         {
895                                                                                 
896                                                                                 case INITIAL: 
897                                                                                         filter.Append((System.String) itr.Current);
898                                                                                         filter.Append('*');
899                                                                                         noStarLast = false;
900                                                                                         break;
901                                                                                 
902                                                                                 case ANY: 
903                                                                                         if (noStarLast)
904                                                                                                 filter.Append('*');
905                                                                                         filter.Append((System.String) itr.Current);
906                                                                                         filter.Append('*');
907                                                                                         noStarLast = false;
908                                                                                         break;
909                                                                                 
910                                                                                 case FINAL: 
911                                                                                         if (noStarLast)
912                                                                                                 filter.Append('*');
913                                                                                         filter.Append((System.String) itr.Current);
914                                                                                         break;
915                                                                                 }
916                                                                 }
917                                                                 break;
918                                                         }
919                                                 }
920                                 }
921                                 else if (filterpart is System.Collections.IEnumerator)
922                                 {
923                                         stringFilter((System.Collections.IEnumerator) filterpart, filter);
924                                 }
925                         }
926                         filter.Append(')');
927                 }
928                 
929                 /// <summary> Convert a UTF8 encoded string, or binary data, into a String encoded for
930                 /// a string filter.
931                 /// </summary>
932                 private static System.String byteString(sbyte[] value_Renamed)
933                 {
934                         System.String toReturn = null;
935                         if (Novell.Directory.Ldap.Utilclass.Base64.isValidUTF8(value_Renamed, true))
936                         {
937                                 try
938                                 {
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);
942
943 //                                      toReturn = new String(value_Renamed, "UTF-8");
944                                 }
945                                 catch (System.IO.IOException e)
946                                 {
947                                         throw new System.SystemException("Default JVM does not support UTF-8 encoding" + e);
948                                 }
949                         }
950                         else
951                         {
952                                 System.Text.StringBuilder binary = new System.Text.StringBuilder();
953                                 for (int i = 0; i < value_Renamed.Length; i++)
954                                 {
955                                         //TODO repair binary output
956                                         //Every octet needs to be escaped
957                                         if (value_Renamed[i] >= 0)
958                                         {
959                                                 //one character hex string
960                                                 binary.Append("\\0");
961                                                 binary.Append(System.Convert.ToString(value_Renamed[i], 16));
962                                         }
963                                         else
964                                         {
965                                                 //negative (eight character) hex string
966                                                 binary.Append("\\" + System.Convert.ToString(value_Renamed[i], 16).Substring(6));
967                                         }
968                                 }
969                                 toReturn = binary.ToString();
970                         }
971                         return toReturn;
972                 }
973                 
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.
979                 /// </summary>
980                 private class FilterIterator : System.Collections.IEnumerator
981                 {
982                         public void Reset(){}
983                         private void  InitBlock(RfcFilter enclosingInstance)
984                         {
985                                 this.enclosingInstance = enclosingInstance;
986                         }
987                         private RfcFilter enclosingInstance;
988                         /// <summary> Returns filter identifiers and components of a filter.
989                         /// 
990                         /// The first object returned is an Integer identifying
991                         /// its type.
992                         /// </summary>
993                         public virtual System.Object Current
994                         {
995                                 get
996                                 {
997                                         System.Object toReturn = null;
998                                         if (!tagReturned)
999                                         {
1000                                                 tagReturned = true;
1001                                                 toReturn = root.getIdentifier().Tag;
1002                                         }
1003                                         else
1004                                         {
1005                                                 Asn1Object asn1 = root.taggedValue();
1006                                                 
1007                                                 if (asn1 is RfcLdapString)
1008                                                 {
1009                                                         //one value to iterate
1010                                                         hasMore = false;
1011                                                         toReturn = ((RfcLdapString) asn1).stringValue();
1012                                                 }
1013                                                 else if (asn1 is RfcSubstringFilter)
1014                                                 {
1015                                                         
1016                                                         RfcSubstringFilter sub = (RfcSubstringFilter) asn1;
1017                                                         if (index == - 1)
1018                                                         {
1019                                                                 //return attribute name
1020                                                                 index = 0;
1021                                                                 RfcAttributeDescription attr = (RfcAttributeDescription) sub.get_Renamed(0);
1022                                                                 toReturn = attr.stringValue();
1023                                                         }
1024                                                         else if (index % 2 == 0)
1025                                                         {
1026                                                                 //return substring identifier
1027                                                                 Asn1SequenceOf substrs = (Asn1SequenceOf) sub.get_Renamed(1);
1028                                                                 toReturn = ((Asn1Tagged) substrs.get_Renamed(index / 2)).getIdentifier().Tag;
1029                                                                 index++;
1030                                                         }
1031                                                         else
1032                                                         {
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();
1038                                                                 index++;
1039                                                         }
1040                                                         if (index / 2 >= ((Asn1SequenceOf) sub.get_Renamed(1)).size())
1041                                                         {
1042                                                                 hasMore = false;
1043                                                         }
1044                                                 }
1045                                                 else if (asn1 is RfcAttributeValueAssertion)
1046                                                 {
1047                                                         // components: =,>=,<=,~=
1048                                                         RfcAttributeValueAssertion assertion = (RfcAttributeValueAssertion) asn1;
1049                                                         
1050                                                         if (index == - 1)
1051                                                         {
1052                                                                 toReturn = assertion.AttributeDescription;
1053                                                                 index = 1;
1054                                                         }
1055                                                         else if (index == 1)
1056                                                         {
1057                                                                 toReturn = assertion.AssertionValue;
1058                                                                 index = 2;
1059                                                                 hasMore = false;
1060                                                         }
1061                                                 }
1062                                                 else if (asn1 is RfcMatchingRuleAssertion)
1063                                                 {
1064                                                         //Extensible match
1065                                                         RfcMatchingRuleAssertion exMatch = (RfcMatchingRuleAssertion) asn1;
1066                                                         if (index == - 1)
1067                                                         {
1068                                                                 index = 0;
1069                                                         }
1070                                                         toReturn = ((Asn1OctetString) ((Asn1Tagged) exMatch.get_Renamed(index++)).taggedValue()).stringValue();
1071                                                         if (index > 2)
1072                                                         {
1073                                                                 hasMore = false;
1074                                                         }
1075                                                 }
1076                                                 else if (asn1 is Asn1SetOf)
1077                                                 {
1078                                                         //AND and OR nested components
1079                                                         Asn1SetOf set_Renamed = (Asn1SetOf) asn1;
1080                                                         if (index == - 1)
1081                                                         {
1082                                                                 index = 0;
1083                                                         }
1084                                                         toReturn = new FilterIterator(enclosingInstance,(Asn1Tagged) set_Renamed.get_Renamed(index++));
1085                                                         if (index >= set_Renamed.size())
1086                                                         {
1087                                                                 this.hasMore = false;
1088                                                         }
1089                                                 }
1090                                                 else if (asn1 is Asn1Tagged)
1091                                                 {
1092                                                         //NOT nested component.
1093                                                         toReturn = new FilterIterator(enclosingInstance,(Asn1Tagged) asn1);
1094                                                         this.hasMore = false;
1095                                                 }
1096                                         }
1097                                         return toReturn;
1098                                 }
1099                                 
1100                         }
1101                         public RfcFilter Enclosing_Instance
1102                         {
1103                                 get
1104                                 {
1105                                         return enclosingInstance;
1106                                 }
1107                                 
1108                         }
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;
1115                         
1116                         public FilterIterator(RfcFilter enclosingInstance, Asn1Tagged root)
1117                         {
1118                                 InitBlock(enclosingInstance);
1119                                 this.root = root;
1120                         }
1121                         public virtual bool MoveNext()
1122                         {
1123                                 return hasMore;
1124                         }
1125                         
1126                         public void  remove()
1127                         {
1128                                 throw new System.NotSupportedException("Remove is not supported on a filter iterator");
1129                         }
1130                 }
1131                 
1132                 /// <summary> This inner class will tokenize the components of an RFC 2254 search filter.</summary>
1133                 internal class FilterTokenizer
1134                 {
1135                         private void  InitBlock(RfcFilter enclosingInstance)
1136                         {
1137                                 this.enclosingInstance = enclosingInstance;
1138                         }
1139                         private RfcFilter enclosingInstance;
1140                         /// <summary> Reads either an operator, or an attribute, whichever is
1141                         /// next in the filter string.
1142                         /// 
1143                         /// 
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
1147                         /// returned.
1148                         /// </summary>
1149                         virtual public int OpOrAttr
1150                         {
1151                                 get
1152                                 {
1153                                         int index;
1154                                         
1155                                         if (offset >= filterLength)
1156                                         {
1157                                                 //"Unexpected end of filter",
1158                                                 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1159                                         }
1160                                         int ret;
1161                                         int testChar = filter[offset];
1162                                         if (testChar == '&')
1163                                         {
1164                                                 offset++;
1165                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.AND;
1166                                         }
1167                                         else if (testChar == '|')
1168                                         {
1169                                                 offset++;
1170                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.OR;
1171                                         }
1172                                         else if (testChar == '!')
1173                                         {
1174                                                 offset++;
1175                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.NOT;
1176                                         }
1177                                         else
1178                                         {
1179                                                 if (filter.Substring(offset).StartsWith(":=") == true)
1180                                                 {
1181                                                         throw new LdapLocalException(ExceptionMessages.NO_MATCHING_RULE, LdapException.FILTER_ERROR);
1182                                                 }
1183                                                 
1184                                                 if (filter.Substring(offset).StartsWith("::=") == true || filter.Substring(offset).StartsWith(":::=") == true)
1185                                                 {
1186                                                         throw new LdapLocalException(ExceptionMessages.NO_DN_NOR_MATCHING_RULE, LdapException.FILTER_ERROR);
1187                                                 }
1188                                                 
1189                                                 
1190                                                 // get first component of 'item' (attr or :dn or :matchingrule)
1191                                                 System.String delims = "=~<>()";
1192                                                 System.Text.StringBuilder sb = new System.Text.StringBuilder();
1193                                                 
1194                                                 while (delims.IndexOf((System.Char) filter[offset]) == - 1 && filter.Substring(offset).StartsWith(":=") == false)
1195                                                 {
1196                                                         sb.Append(filter[offset++]);
1197                                                 }
1198                                                 
1199                                                 attr = sb.ToString().Trim();
1200                                                 
1201                                                 // is there an attribute name specified in the filter ?
1202                                                 if (attr.Length == 0 || attr[0] == ';')
1203                                                 {
1204                                                         throw new LdapLocalException(ExceptionMessages.NO_ATTRIBUTE_NAME, LdapException.FILTER_ERROR);
1205                                                 }
1206                                                 
1207                                                 for (index = 0; index < attr.Length; index++)
1208                                                 {
1209                                                         char atIndex = attr[index];
1210                                                         if (!(System.Char.IsLetterOrDigit(atIndex) || atIndex == '-' || atIndex == '.' || atIndex == ';' || atIndex == ':'))
1211                                                         {
1212                                                                 
1213                                                                 if (atIndex == '\\')
1214                                                                 {
1215                                                                         throw new LdapLocalException(ExceptionMessages.INVALID_ESC_IN_DESCR, LdapException.FILTER_ERROR);
1216                                                                 }
1217                                                                 else
1218                                                                 {
1219                                                                         throw new LdapLocalException(ExceptionMessages.INVALID_CHAR_IN_DESCR, new System.Object[]{atIndex}, LdapException.FILTER_ERROR);
1220                                                                 }
1221                                                         }
1222                                                 }
1223                                                 
1224                                                 // is there an option specified in the filter ?
1225                                                 index = attr.IndexOf((System.Char) ';');
1226                                                 if (index != - 1 && index == attr.Length - 1)
1227                                                 {
1228                                                         throw new LdapLocalException(ExceptionMessages.NO_OPTION, LdapException.FILTER_ERROR);
1229                                                 }
1230                                                 ret = - 1;
1231                                         }
1232                                         return ret;
1233                                 }
1234                                 
1235                         }
1236                         /// <summary> Reads an RFC 2251 filter type from the filter string and returns its
1237                         /// int value.
1238                         /// </summary>
1239                         virtual public int FilterType
1240                         {
1241                                 get
1242                                 {
1243                                         if (offset >= filterLength)
1244                                         {
1245                                                 //"Unexpected end of filter",
1246                                                 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1247                                         }
1248                                         int ret;
1249                                         if (filter.Substring(offset).StartsWith(">="))
1250                                         {
1251                                                 offset += 2;
1252                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.GREATER_OR_EQUAL;
1253                                         }
1254                                         else if (filter.Substring(offset).StartsWith("<="))
1255                                         {
1256                                                 offset += 2;
1257                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.LESS_OR_EQUAL;
1258                                         }
1259                                         else if (filter.Substring(offset).StartsWith("~="))
1260                                         {
1261                                                 offset += 2;
1262                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.APPROX_MATCH;
1263                                         }
1264                                         else if (filter.Substring(offset).StartsWith(":="))
1265                                         {
1266                                                 offset += 2;
1267                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.EXTENSIBLE_MATCH;
1268                                         }
1269                                         else if (filter[offset] == '=')
1270                                         {
1271                                                 offset++;
1272                                                 ret = Novell.Directory.Ldap.Rfc2251.RfcFilter.EQUALITY_MATCH;
1273                                         }
1274                                         else
1275                                         {
1276                                                 //"Invalid comparison operator",
1277                                                 throw new LdapLocalException(ExceptionMessages.INVALID_FILTER_COMPARISON, LdapException.FILTER_ERROR);
1278                                         }
1279                                         return ret;
1280                                 }
1281                                 
1282                         }
1283                         /// <summary> Reads a value from a filter string.</summary>
1284                         virtual public System.String Value
1285                         {
1286                                 get
1287                                 {
1288                                         if (offset >= filterLength)
1289                                         {
1290                                                 //"Unexpected end of filter",
1291                                                 throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1292                                         }
1293                                         
1294                                         int idx = filter.IndexOf((System.Char) ')', offset);
1295                                         if (idx == - 1)
1296                                         {
1297                                                 idx = filterLength;
1298                                         }
1299                                         System.String ret = filter.Substring(offset, (idx) - (offset));
1300                                         offset = idx;
1301                                         
1302                                         return ret;
1303                                 }
1304                                 
1305                         }
1306                         /// <summary> Returns the current attribute identifier.</summary>
1307                         virtual public System.String Attr
1308                         {
1309                                 get
1310                                 {
1311                                         return attr;
1312                                 }
1313                                 
1314                         }
1315                         public RfcFilter Enclosing_Instance
1316                         {
1317                                 get
1318                                 {
1319                                         return enclosingInstance;
1320                                 }
1321                                 
1322                         }
1323                         
1324                         //*************************************************************************
1325                         // Private variables
1326                         //*************************************************************************
1327                         
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
1332                         
1333                         //*************************************************************************
1334                         // Constructor
1335                         //*************************************************************************
1336                         
1337                         /// <summary> Constructs a FilterTokenizer for a filter.</summary>
1338                         public FilterTokenizer(RfcFilter enclosingInstance, System.String filter)
1339                         {
1340                                 InitBlock(enclosingInstance);
1341                                 this.filter = filter;
1342                                 this.offset = 0;
1343                                 this.filterLength = filter.Length;
1344                                 return ;
1345                         }
1346                         
1347                         //*************************************************************************
1348                         // Tokenizer methods
1349                         //*************************************************************************
1350                         
1351                         /// <summary> Reads the current char and throws an Exception if it is not a left
1352                         /// parenthesis.
1353                         /// </summary>
1354                         public void  getLeftParen()
1355                         {
1356                                 if (offset >= filterLength)
1357                                 {
1358                                         //"Unexpected end of filter",
1359                                         throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1360                                 }
1361                                 if (filter[offset++] != '(')
1362                                 {
1363                                         //"Missing left paren",
1364                                         throw new LdapLocalException(ExceptionMessages.EXPECTING_LEFT_PAREN, new System.Object[]{filter[offset -= 1]}, LdapException.FILTER_ERROR);
1365                                 }
1366                                 return ;
1367                         }
1368                         
1369                         /// <summary> Reads the current char and throws an Exception if it is not a right
1370                         /// parenthesis.
1371                         /// </summary>
1372                         public void  getRightParen()
1373                         {
1374                                 if (offset >= filterLength)
1375                                 {
1376                                         //"Unexpected end of filter",
1377                                         throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1378                                 }
1379                                 if (filter[offset++] != ')')
1380                                 {
1381                                         //"Missing right paren",
1382                                         throw new LdapLocalException(ExceptionMessages.EXPECTING_RIGHT_PAREN, new System.Object[]{filter[offset - 1]}, LdapException.FILTER_ERROR);
1383                                 }
1384                                 return ;
1385                         }
1386                         
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.
1390                         /// </summary>
1391                         public char peekChar()
1392                         {
1393                                 if (offset >= filterLength)
1394                                 {
1395                                         //"Unexpected end of filter",
1396                                         throw new LdapLocalException(ExceptionMessages.UNEXPECTED_END, LdapException.FILTER_ERROR);
1397                                 }
1398                                 return filter[offset];
1399                         }
1400                 }
1401         }
1402 }