1/*
2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.jndi.ldap;
27
28import javax.naming.NamingException;
29import javax.naming.directory.InvalidSearchFilterException;
30
31import java.io.IOException;
32
33/**
34 * LDAP (RFC-1960) and LDAPv3 (RFC-2254) search filters.
35 *
36 * @author Xuelei Fan
37 * @author Vincent Ryan
38 * @author Jagane Sundar
39 * @author Rosanna Lee
40 */
41
42final class Filter {
43
44    /**
45     * First convert filter string into byte[].
46     * For LDAP v3, the conversion uses Unicode -> UTF8
47     * For LDAP v2, the conversion uses Unicode -> ISO 8859 (Latin-1)
48     *
49     * Then parse the byte[] as a filter, converting \hh to
50     * a single byte, and encoding the resulting filter
51     * into the supplied BER buffer
52     */
53    static void encodeFilterString(BerEncoder ber, String filterStr,
54        boolean isLdapv3) throws IOException, NamingException {
55
56        if ((filterStr == null) || (filterStr.equals(""))) {
57            throw new InvalidSearchFilterException("Empty filter");
58        }
59        byte[] filter;
60        int filterLen;
61        if (isLdapv3) {
62            filter = filterStr.getBytes("UTF8");
63        } else {
64            filter = filterStr.getBytes("8859_1");
65        }
66        filterLen = filter.length;
67        if (dbg) {
68            dbgIndent = 0;
69            System.err.println("String filter: " + filterStr);
70            System.err.println("size: " + filterLen);
71            dprint("original: ", filter, 0, filterLen);
72        }
73
74        encodeFilter(ber, filter, 0, filterLen);
75    }
76
77    private static void encodeFilter(BerEncoder ber, byte[] filter,
78        int filterStart, int filterEnd) throws IOException, NamingException {
79
80        if (dbg) {
81            dprint("encFilter: ",  filter, filterStart, filterEnd);
82            dbgIndent++;
83        }
84
85        if ((filterEnd - filterStart) <= 0) {
86            throw new InvalidSearchFilterException("Empty filter");
87        }
88
89        int nextOffset;
90        int parens, balance;
91        boolean escape;
92
93        parens = 0;
94
95        int filtOffset[] = new int[1];
96
97        for (filtOffset[0] = filterStart; filtOffset[0] < filterEnd;) {
98            switch (filter[filtOffset[0]]) {
99            case '(':
100                filtOffset[0]++;
101                parens++;
102                switch (filter[filtOffset[0]]) {
103                case '&':
104                    encodeComplexFilter(ber, filter,
105                        LDAP_FILTER_AND, filtOffset, filterEnd);
106                    // filtOffset[0] has pointed to char after right paren
107                    parens--;
108                    break;
109
110                case '|':
111                    encodeComplexFilter(ber, filter,
112                        LDAP_FILTER_OR, filtOffset, filterEnd);
113                    // filtOffset[0] has pointed to char after right paren
114                    parens--;
115                    break;
116
117                case '!':
118                    encodeComplexFilter(ber, filter,
119                        LDAP_FILTER_NOT, filtOffset, filterEnd);
120                    // filtOffset[0] has pointed to char after right paren
121                    parens--;
122                    break;
123
124                default:
125                    balance = 1;
126                    escape = false;
127                    nextOffset = filtOffset[0];
128                    while (nextOffset < filterEnd && balance > 0) {
129                        if (!escape) {
130                            if (filter[nextOffset] == '(')
131                                balance++;
132                            else if (filter[nextOffset] == ')')
133                                balance--;
134                        }
135                        if (filter[nextOffset] == '\\' && !escape)
136                            escape = true;
137                        else
138                            escape = false;
139                        if (balance > 0)
140                            nextOffset++;
141                    }
142                    if (balance != 0)
143                        throw new InvalidSearchFilterException(
144                                  "Unbalanced parenthesis");
145
146                    encodeSimpleFilter(ber, filter, filtOffset[0], nextOffset);
147
148                    // points to the char after right paren.
149                    filtOffset[0] = nextOffset + 1;
150
151                    parens--;
152                    break;
153
154                }
155                break;
156
157            case ')':
158                //
159                // End of sequence
160                //
161                ber.endSeq();
162                filtOffset[0]++;
163                parens--;
164                break;
165
166            case ' ':
167                filtOffset[0]++;
168                break;
169
170            default:    // assume simple type=value filter
171                encodeSimpleFilter(ber, filter, filtOffset[0], filterEnd);
172                filtOffset[0] = filterEnd; // force break from outer
173                break;
174            }
175
176            if (parens < 0) {
177                throw new InvalidSearchFilterException(
178                                                "Unbalanced parenthesis");
179            }
180        }
181
182        if (parens != 0) {
183            throw new InvalidSearchFilterException("Unbalanced parenthesis");
184        }
185
186        if (dbg) {
187            dbgIndent--;
188        }
189
190    }
191
192    /**
193     * convert character 'c' that represents a hexadecimal digit to an integer.
194     * if 'c' is not a hexadecimal digit [0-9A-Fa-f], -1 is returned.
195     * otherwise the converted value is returned.
196     */
197    private static int hexchar2int( byte c ) {
198        if ( c >= '0' && c <= '9' ) {
199            return( c - '0' );
200        }
201        if ( c >= 'A' && c <= 'F' ) {
202            return( c - 'A' + 10 );
203        }
204        if ( c >= 'a' && c <= 'f' ) {
205            return( c - 'a' + 10 );
206        }
207        return( -1 );
208    }
209
210    // called by the LdapClient.compare method
211    static byte[] unescapeFilterValue(byte[] orig, int start, int end)
212        throws NamingException {
213        boolean escape = false, escStart = false;
214        int ival;
215        byte ch;
216
217        if (dbg) {
218            dprint("unescape: " , orig, start, end);
219        }
220
221        int len = end - start;
222        byte tbuf[] = new byte[len];
223        int j = 0;
224        for (int i = start; i < end; i++) {
225            ch = orig[i];
226            if (escape) {
227                // Try LDAP V3 escape (\xx)
228                if ((ival = hexchar2int(ch)) < 0) {
229
230                    /**
231                     * If there is no hex char following a '\' when
232                     * parsing a LDAP v3 filter (illegal by v3 way)
233                     * we fallback to the way we unescape in v2.
234                     */
235                    if (escStart) {
236                        // V2: \* \( \)
237                        escape = false;
238                        tbuf[j++] = ch;
239                    } else {
240                        // escaping already started but we can't find 2nd hex
241                        throw new InvalidSearchFilterException("invalid escape sequence: " + orig);
242                    }
243                } else {
244                    if (escStart) {
245                        tbuf[j] = (byte)(ival<<4);
246                        escStart = false;
247                    } else {
248                        tbuf[j++] |= (byte)ival;
249                        escape = false;
250                    }
251                }
252            } else if (ch != '\\') {
253                tbuf[j++] = ch;
254                escape = false;
255            } else {
256                escStart = escape = true;
257            }
258        }
259        byte[] answer = new byte[j];
260        System.arraycopy(tbuf, 0, answer, 0, j);
261        if (dbg) {
262            Ber.dumpBER(System.err, "", answer, 0, j);
263        }
264        return answer;
265    }
266
267    private static int indexOf(byte[] str, char ch, int start, int end) {
268        for (int i = start; i < end; i++) {
269            if (str[i] == ch)
270                return i;
271        }
272        return -1;
273    }
274
275    private static int indexOf(byte[] str, String target, int start, int end) {
276        int where = indexOf(str, target.charAt(0), start, end);
277        if (where >= 0) {
278            for (int i = 1; i < target.length(); i++) {
279                if (str[where+i] != target.charAt(i)) {
280                    return -1;
281                }
282            }
283        }
284        return where;
285    }
286
287    private static int findUnescaped(byte[] str, char ch, int start, int end) {
288        while (start < end) {
289            int where = indexOf(str, ch, start, end);
290
291            /*
292             * Count the immediate preceding '\' to find out if
293             * this is an escaped '*'. This is a made-up way for
294             * parsing an escaped '*' in v2. This is how the other leading
295             * SDK vendors interpret v2.
296             * For v3 we fallback to the way we parse "\*" in v2.
297             * It's not legal in v3 to use "\*" to escape '*'; the right
298             * way is to use "\2a" instead.
299             */
300            int backSlashPos;
301            int backSlashCnt = 0;
302            for (backSlashPos = where - 1;
303                    ((backSlashPos >= start) && (str[backSlashPos] == '\\'));
304                    backSlashPos--, backSlashCnt++);
305
306            // if at start of string, or not there at all, or if not escaped
307            if (where == start || where == -1 || ((backSlashCnt % 2) == 0))
308                return where;
309
310            // start search after escaped star
311            start = where + 1;
312        }
313        return -1;
314    }
315
316
317    private static void encodeSimpleFilter(BerEncoder ber, byte[] filter,
318        int filtStart, int filtEnd) throws IOException, NamingException {
319
320        if (dbg) {
321            dprint("encSimpleFilter: ", filter, filtStart, filtEnd);
322            dbgIndent++;
323        }
324
325        String type, value;
326        int valueStart, valueEnd, typeStart, typeEnd;
327
328        int eq;
329        if ((eq = indexOf(filter, '=', filtStart, filtEnd)) == -1) {
330            throw new InvalidSearchFilterException("Missing 'equals'");
331        }
332
333
334        valueStart = eq + 1;        // value starts after equal sign
335        valueEnd = filtEnd;
336        typeStart = filtStart;      // beginning of string
337
338        int ftype;
339
340        switch (filter[eq - 1]) {
341        case '<':
342            ftype = LDAP_FILTER_LE;
343            typeEnd = eq - 1;
344            break;
345        case '>':
346            ftype = LDAP_FILTER_GE;
347            typeEnd = eq - 1;
348            break;
349        case '~':
350            ftype = LDAP_FILTER_APPROX;
351            typeEnd = eq - 1;
352            break;
353        case ':':
354            ftype = LDAP_FILTER_EXT;
355            typeEnd = eq - 1;
356            break;
357        default:
358            typeEnd = eq;
359            //initializing ftype to make the compiler happy
360            ftype = 0x00;
361            break;
362        }
363
364        if (dbg) {
365            System.err.println("type: " + typeStart + ", " + typeEnd);
366            System.err.println("value: " + valueStart + ", " + valueEnd);
367        }
368
369        // check validity of type
370        //
371        // RFC4512 defines the type as the following ABNF:
372        //     attr = attributedescription
373        //     attributedescription = attributetype options
374        //     attributetype = oid
375        //     oid = descr / numericoid
376        //     descr = keystring
377        //     keystring = leadkeychar *keychar
378        //     leadkeychar = ALPHA
379        //     keychar = ALPHA / DIGIT / HYPHEN
380        //     numericoid = number 1*( DOT number )
381        //     number  = DIGIT / ( LDIGIT 1*DIGIT )
382        //     options = *( SEMI option )
383        //     option = 1*keychar
384        //
385        // And RFC4515 defines the extensible type as the following ABNF:
386        //     attr [dnattrs] [matchingrule] / [dnattrs] matchingrule
387        int optionsStart = -1;
388        int extensibleStart = -1;
389        if ((filter[typeStart] >= '0' && filter[typeStart] <= '9') ||
390            (filter[typeStart] >= 'A' && filter[typeStart] <= 'Z') ||
391            (filter[typeStart] >= 'a' && filter[typeStart] <= 'z')) {
392
393            boolean isNumericOid =
394                filter[typeStart] >= '0' && filter[typeStart] <= '9';
395            for (int i = typeStart + 1; i < typeEnd; i++) {
396                // ';' is an indicator of attribute options
397                if (filter[i] == ';') {
398                    if (isNumericOid && filter[i - 1] == '.') {
399                        throw new InvalidSearchFilterException(
400                                    "invalid attribute description");
401                    }
402
403                    // attribute options
404                    optionsStart = i;
405                    break;
406                }
407
408                // ':' is an indicator of extensible rules
409                if (filter[i] == ':' && ftype == LDAP_FILTER_EXT) {
410                    if (isNumericOid && filter[i - 1] == '.') {
411                        throw new InvalidSearchFilterException(
412                                    "invalid attribute description");
413                    }
414
415                    // extensible matching
416                    extensibleStart = i;
417                    break;
418                }
419
420                if (isNumericOid) {
421                    // numeric object identifier
422                    if ((filter[i] == '.' && filter[i - 1] == '.') ||
423                        (filter[i] != '.' &&
424                            !(filter[i] >= '0' && filter[i] <= '9'))) {
425                        throw new InvalidSearchFilterException(
426                                    "invalid attribute description");
427                    }
428                } else {
429                    // descriptor
430                    // The underscore ("_") character is not allowed by
431                    // the LDAP specification. We allow it here to
432                    // tolerate the incorrect use in practice.
433                    if (filter[i] != '-' && filter[i] != '_' &&
434                        !(filter[i] >= '0' && filter[i] <= '9') &&
435                        !(filter[i] >= 'A' && filter[i] <= 'Z') &&
436                        !(filter[i] >= 'a' && filter[i] <= 'z')) {
437                        throw new InvalidSearchFilterException(
438                                    "invalid attribute description");
439                    }
440                }
441            }
442        } else if (ftype == LDAP_FILTER_EXT && filter[typeStart] == ':') {
443            // extensible matching
444            extensibleStart = typeStart;
445        } else {
446            throw new InvalidSearchFilterException(
447                                    "invalid attribute description");
448        }
449
450        // check attribute options
451        if (optionsStart > 0) {
452            for (int i = optionsStart + 1; i < typeEnd; i++) {
453                if (filter[i] == ';') {
454                    if (filter[i - 1] == ';') {
455                        throw new InvalidSearchFilterException(
456                                    "invalid attribute description");
457                    }
458                    continue;
459                }
460
461                // ':' is an indicator of extensible rules
462                if (filter[i] == ':' && ftype == LDAP_FILTER_EXT) {
463                    if (filter[i - 1] == ';') {
464                        throw new InvalidSearchFilterException(
465                                    "invalid attribute description");
466                    }
467
468                    // extensible matching
469                    extensibleStart = i;
470                    break;
471                }
472
473                // The underscore ("_") character is not allowed by
474                // the LDAP specification. We allow it here to
475                // tolerate the incorrect use in practice.
476                if (filter[i] != '-' && filter[i] != '_' &&
477                        !(filter[i] >= '0' && filter[i] <= '9') &&
478                        !(filter[i] >= 'A' && filter[i] <= 'Z') &&
479                        !(filter[i] >= 'a' && filter[i] <= 'z')) {
480                    throw new InvalidSearchFilterException(
481                                    "invalid attribute description");
482                }
483            }
484        }
485
486        // check extensible matching
487        if (extensibleStart > 0) {
488            boolean isMatchingRule = false;
489            for (int i = extensibleStart + 1; i < typeEnd; i++) {
490                if (filter[i] == ':') {
491                    throw new InvalidSearchFilterException(
492                                    "invalid attribute description");
493                } else if ((filter[i] >= '0' && filter[i] <= '9') ||
494                           (filter[i] >= 'A' && filter[i] <= 'Z') ||
495                           (filter[i] >= 'a' && filter[i] <= 'z')) {
496                    boolean isNumericOid = filter[i] >= '0' && filter[i] <= '9';
497                    i++;
498                    for (int j = i; j < typeEnd; j++, i++) {
499                        // allows no more than two extensible rules
500                        if (filter[j] == ':') {
501                            if (isMatchingRule) {
502                                throw new InvalidSearchFilterException(
503                                            "invalid attribute description");
504                            }
505                            if (isNumericOid && filter[j - 1] == '.') {
506                                throw new InvalidSearchFilterException(
507                                            "invalid attribute description");
508                            }
509
510                            isMatchingRule = true;
511                            break;
512                        }
513
514                        if (isNumericOid) {
515                            // numeric object identifier
516                            if ((filter[j] == '.' && filter[j - 1] == '.') ||
517                                (filter[j] != '.' &&
518                                    !(filter[j] >= '0' && filter[j] <= '9'))) {
519                                throw new InvalidSearchFilterException(
520                                            "invalid attribute description");
521                            }
522                        } else {
523                            // descriptor
524                            // The underscore ("_") character is not allowed by
525                            // the LDAP specification. We allow it here to
526                            // tolerate the incorrect use in practice.
527                            if (filter[j] != '-' && filter[j] != '_' &&
528                                !(filter[j] >= '0' && filter[j] <= '9') &&
529                                !(filter[j] >= 'A' && filter[j] <= 'Z') &&
530                                !(filter[j] >= 'a' && filter[j] <= 'z')) {
531                                throw new InvalidSearchFilterException(
532                                            "invalid attribute description");
533                            }
534                        }
535                    }
536                } else {
537                    throw new InvalidSearchFilterException(
538                                    "invalid attribute description");
539                }
540            }
541        }
542
543        // ensure the latest byte is not isolated
544        if (filter[typeEnd - 1] == '.' || filter[typeEnd - 1] == ';' ||
545                                          filter[typeEnd - 1] == ':') {
546            throw new InvalidSearchFilterException(
547                "invalid attribute description");
548        }
549
550        if (typeEnd == eq) { // filter type is of "equal"
551            if (findUnescaped(filter, '*', valueStart, valueEnd) == -1) {
552                ftype = LDAP_FILTER_EQUALITY;
553            } else if (filter[valueStart] == '*' &&
554                            valueStart == (valueEnd - 1)) {
555                ftype = LDAP_FILTER_PRESENT;
556            } else {
557                encodeSubstringFilter(ber, filter,
558                    typeStart, typeEnd, valueStart, valueEnd);
559                return;
560            }
561        }
562
563        if (ftype == LDAP_FILTER_PRESENT) {
564            ber.encodeOctetString(filter, ftype, typeStart, typeEnd-typeStart);
565        } else if (ftype == LDAP_FILTER_EXT) {
566            encodeExtensibleMatch(ber, filter,
567                typeStart, typeEnd, valueStart, valueEnd);
568        } else {
569            ber.beginSeq(ftype);
570                ber.encodeOctetString(filter, Ber.ASN_OCTET_STR,
571                    typeStart, typeEnd - typeStart);
572                ber.encodeOctetString(
573                    unescapeFilterValue(filter, valueStart, valueEnd),
574                    Ber.ASN_OCTET_STR);
575            ber.endSeq();
576        }
577
578        if (dbg) {
579            dbgIndent--;
580        }
581    }
582
583    private static void encodeSubstringFilter(BerEncoder ber, byte[] filter,
584        int typeStart, int typeEnd, int valueStart, int valueEnd)
585        throws IOException, NamingException {
586
587        if (dbg) {
588            dprint("encSubstringFilter: type ", filter, typeStart, typeEnd);
589            dprint(", val : ", filter, valueStart, valueEnd);
590            dbgIndent++;
591        }
592
593        ber.beginSeq(LDAP_FILTER_SUBSTRINGS);
594            ber.encodeOctetString(filter, Ber.ASN_OCTET_STR,
595                    typeStart, typeEnd-typeStart);
596            ber.beginSeq(LdapClient.LBER_SEQUENCE);
597                int index;
598                int previndex = valueStart;
599                while ((index = findUnescaped(filter, '*', previndex, valueEnd)) != -1) {
600                    if (previndex == valueStart) {
601                      if (previndex < index) {
602                          if (dbg)
603                              System.err.println(
604                                  "initial: " + previndex + "," + index);
605                        ber.encodeOctetString(
606                            unescapeFilterValue(filter, previndex, index),
607                            LDAP_SUBSTRING_INITIAL);
608                      }
609                    } else {
610                      if (previndex < index) {
611                          if (dbg)
612                              System.err.println("any: " + previndex + "," + index);
613                        ber.encodeOctetString(
614                            unescapeFilterValue(filter, previndex, index),
615                            LDAP_SUBSTRING_ANY);
616                      }
617                    }
618                    previndex = index + 1;
619                }
620                if (previndex < valueEnd) {
621                    if (dbg)
622                        System.err.println("final: " + previndex + "," + valueEnd);
623                  ber.encodeOctetString(
624                      unescapeFilterValue(filter, previndex, valueEnd),
625                      LDAP_SUBSTRING_FINAL);
626                }
627            ber.endSeq();
628        ber.endSeq();
629
630        if (dbg) {
631            dbgIndent--;
632        }
633    }
634
635    // The complex filter types look like:
636    //     "&(type=val)(type=val)"
637    //     "|(type=val)(type=val)"
638    //     "!(type=val)"
639    //
640    // The filtOffset[0] pointing to the '&', '|', or '!'.
641    //
642    private static void encodeComplexFilter(BerEncoder ber, byte[] filter,
643        int filterType, int filtOffset[], int filtEnd)
644        throws IOException, NamingException {
645
646        if (dbg) {
647            dprint("encComplexFilter: ", filter, filtOffset[0], filtEnd);
648            dprint(", type: " + Integer.toString(filterType, 16));
649            dbgIndent++;
650        }
651
652        filtOffset[0]++;
653
654        ber.beginSeq(filterType);
655
656            int[] parens = findRightParen(filter, filtOffset, filtEnd);
657            encodeFilterList(ber, filter, filterType, parens[0], parens[1]);
658
659        ber.endSeq();
660
661        if (dbg) {
662            dbgIndent--;
663        }
664
665    }
666
667    //
668    // filter at filtOffset[0] - 1 points to a (. Find ) that matches it
669    // and return substring between the parens. Adjust filtOffset[0] to
670    // point to char after right paren
671    //
672    private static int[] findRightParen(byte[] filter, int filtOffset[], int end)
673    throws IOException, NamingException {
674
675        int balance = 1;
676        boolean escape = false;
677        int nextOffset = filtOffset[0];
678
679        while (nextOffset < end && balance > 0) {
680            if (!escape) {
681                if (filter[nextOffset] == '(')
682                    balance++;
683                else if (filter[nextOffset] == ')')
684                    balance--;
685            }
686            if (filter[nextOffset] == '\\' && !escape)
687                escape = true;
688            else
689                escape = false;
690            if (balance > 0)
691                nextOffset++;
692        }
693        if (balance != 0) {
694            throw new InvalidSearchFilterException("Unbalanced parenthesis");
695        }
696
697        // String tmp = filter.substring(filtOffset[0], nextOffset);
698
699        int[] tmp = new int[] {filtOffset[0], nextOffset};
700
701        filtOffset[0] = nextOffset + 1;
702
703        return tmp;
704
705    }
706
707    //
708    // Encode filter list of type "(filter1)(filter2)..."
709    //
710    private static void encodeFilterList(BerEncoder ber, byte[] filter,
711        int filterType, int start, int end) throws IOException, NamingException {
712
713        if (dbg) {
714            dprint("encFilterList: ", filter, start, end);
715            dbgIndent++;
716        }
717
718        int filtOffset[] = new int[1];
719        int listNumber = 0;
720        for (filtOffset[0] = start; filtOffset[0] < end; filtOffset[0]++) {
721            if (Character.isSpaceChar((char)filter[filtOffset[0]]))
722                continue;
723
724            if ((filterType == LDAP_FILTER_NOT) && (listNumber > 0)) {
725                throw new InvalidSearchFilterException(
726                    "Filter (!) cannot be followed by more than one filters");
727            }
728
729            if (filter[filtOffset[0]] == '(') {
730                continue;
731            }
732
733            int[] parens = findRightParen(filter, filtOffset, end);
734
735            // add enclosing parens
736            int len = parens[1]-parens[0];
737            byte[] newfilter = new byte[len+2];
738            System.arraycopy(filter, parens[0], newfilter, 1, len);
739            newfilter[0] = (byte)'(';
740            newfilter[len+1] = (byte)')';
741            encodeFilter(ber, newfilter, 0, newfilter.length);
742
743            listNumber++;
744        }
745
746        if (dbg) {
747            dbgIndent--;
748        }
749
750    }
751
752    //
753    // Encode extensible match
754    //
755    private static void encodeExtensibleMatch(BerEncoder ber, byte[] filter,
756        int matchStart, int matchEnd, int valueStart, int valueEnd)
757        throws IOException, NamingException {
758
759        boolean matchDN = false;
760        int colon;
761        int colon2;
762        int i;
763
764        ber.beginSeq(LDAP_FILTER_EXT);
765
766            // test for colon separator
767            if ((colon = indexOf(filter, ':', matchStart, matchEnd)) >= 0) {
768
769                // test for match DN
770                if ((i = indexOf(filter, ":dn", colon, matchEnd)) >= 0) {
771                    matchDN = true;
772                }
773
774                // test for matching rule
775                if (((colon2 = indexOf(filter, ':', colon + 1, matchEnd)) >= 0)
776                    || (i == -1)) {
777
778                    if (i == colon) {
779                        ber.encodeOctetString(filter, LDAP_FILTER_EXT_RULE,
780                            colon2 + 1, matchEnd - (colon2 + 1));
781
782                    } else if ((i == colon2) && (i >= 0)) {
783                        ber.encodeOctetString(filter, LDAP_FILTER_EXT_RULE,
784                            colon + 1, colon2 - (colon + 1));
785
786                    } else {
787                        ber.encodeOctetString(filter, LDAP_FILTER_EXT_RULE,
788                            colon + 1, matchEnd - (colon + 1));
789                    }
790                }
791
792                // test for attribute type
793                if (colon > matchStart) {
794                    ber.encodeOctetString(filter,
795                        LDAP_FILTER_EXT_TYPE, matchStart, colon - matchStart);
796                }
797            } else {
798                ber.encodeOctetString(filter, LDAP_FILTER_EXT_TYPE, matchStart,
799                    matchEnd - matchStart);
800            }
801
802            ber.encodeOctetString(
803                unescapeFilterValue(filter, valueStart, valueEnd),
804                LDAP_FILTER_EXT_VAL);
805
806            /*
807             * This element is defined in RFC-2251 with an ASN.1 DEFAULT tag.
808             * However, for Active Directory interoperability it is transmitted
809             * even when FALSE.
810             */
811            ber.encodeBoolean(matchDN, LDAP_FILTER_EXT_DN);
812
813        ber.endSeq();
814    }
815
816    ////////////////////////////////////////////////////////////////////////////
817    //
818    // some debug print code that does indenting. Useful for debugging
819    // the filter generation code
820    //
821    ////////////////////////////////////////////////////////////////////////////
822
823    private static final boolean dbg = false;
824    private static int dbgIndent = 0;
825
826    private static void dprint(String msg) {
827        dprint(msg, new byte[0], 0, 0);
828    }
829
830    private static void dprint(String msg, byte[] str) {
831        dprint(msg, str, 0, str.length);
832    }
833
834    private static void dprint(String msg, byte[] str, int start, int end) {
835        String dstr = "  ";
836        int i = dbgIndent;
837        while (i-- > 0) {
838            dstr += "  ";
839        }
840        dstr += msg;
841
842        System.err.print(dstr);
843        for (int j = start; j < end; j++) {
844            System.err.print((char)str[j]);
845        }
846        System.err.println();
847    }
848
849    /////////////// Constants used for encoding filter //////////////
850
851    static final int LDAP_FILTER_AND = 0xa0;
852    static final int LDAP_FILTER_OR = 0xa1;
853    static final int LDAP_FILTER_NOT = 0xa2;
854    static final int LDAP_FILTER_EQUALITY = 0xa3;
855    static final int LDAP_FILTER_SUBSTRINGS = 0xa4;
856    static final int LDAP_FILTER_GE = 0xa5;
857    static final int LDAP_FILTER_LE = 0xa6;
858    static final int LDAP_FILTER_PRESENT = 0x87;
859    static final int LDAP_FILTER_APPROX = 0xa8;
860    static final int LDAP_FILTER_EXT = 0xa9;            // LDAPv3
861
862    static final int LDAP_FILTER_EXT_RULE = 0x81;       // LDAPv3
863    static final int LDAP_FILTER_EXT_TYPE = 0x82;       // LDAPv3
864    static final int LDAP_FILTER_EXT_VAL = 0x83;        // LDAPv3
865    static final int LDAP_FILTER_EXT_DN = 0x84;         // LDAPv3
866
867    static final int LDAP_SUBSTRING_INITIAL = 0x80;
868    static final int LDAP_SUBSTRING_ANY = 0x81;
869    static final int LDAP_SUBSTRING_FINAL = 0x82;
870}
871