001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.protocols.ldap;
028 import org.opends.messages.Message;
029
030
031
032 import java.nio.ByteBuffer;
033 import java.util.ArrayList;
034 import java.util.HashSet;
035 import java.util.LinkedList;
036 import java.util.List;
037 import java.util.StringTokenizer;
038 import java.util.Collection;
039
040 import org.opends.server.api.MatchingRule;
041 import org.opends.server.core.DirectoryServer;
042 import org.opends.server.protocols.asn1.ASN1OctetString;
043 import org.opends.server.types.AttributeType;
044 import org.opends.server.types.AttributeValue;
045 import org.opends.server.types.ByteString;
046 import org.opends.server.types.DebugLogLevel;
047 import org.opends.server.types.DirectoryException;
048 import org.opends.server.types.FilterType;
049 import org.opends.server.types.LDAPException;
050 import org.opends.server.types.RawFilter;
051 import org.opends.server.types.ResultCode;
052 import org.opends.server.types.SearchFilter;
053
054 import static org.opends.server.loggers.debug.DebugLogger.*;
055 import org.opends.server.loggers.debug.DebugTracer;
056 import static org.opends.messages.ProtocolMessages.*;
057 import static org.opends.server.protocols.ldap.LDAPConstants.*;
058 import static org.opends.server.protocols.ldap.LDAPResultCode.*;
059 import static org.opends.server.util.StaticUtils.*;
060
061
062
063 /**
064 * This class defines the data structures and methods to use when interacting
065 * with an LDAP search filter, which defines a set of criteria for locating
066 * entries in a search request.
067 */
068 public class LDAPFilter
069 extends RawFilter
070 {
071 /**
072 * The tracer object for the debug logger.
073 */
074 private static final DebugTracer TRACER = getTracer();
075
076 // The set of subAny elements for substring filters.
077 private ArrayList<ByteString> subAnyElements;
078
079 // The set of filter components for AND and OR filters.
080 private ArrayList<RawFilter> filterComponents;
081
082 // Indicates whether to match on DN attributes for extensible match filters.
083 private boolean dnAttributes;
084
085 // The assertion value for several filter types.
086 private ByteString assertionValue;
087
088 // The subFinal element for substring filters.
089 private ByteString subFinalElement;
090
091 // The subInitial element for substring filters.
092 private ByteString subInitialElement;
093
094 // The filter type for this filter.
095 private FilterType filterType;
096
097 // The filter component for NOT filters.
098 private RawFilter notComponent;
099
100 // The attribute type for several filter types.
101 private String attributeType;
102
103 // The matching rule ID for extensible matching filters.
104 private String matchingRuleID;
105
106
107
108 /**
109 * Creates a new LDAP filter with the provided information. Note that this
110 * constructor is only intended for use by the {@code RawFilter} class and any
111 * use of this constructor outside of that class must be very careful to
112 * ensure that all of the appropriate element types have been provided for the
113 * associated filter type.
114 *
115 * @param filterType The filter type for this filter.
116 * @param filterComponents The filter components for AND and OR filters.
117 * @param notComponent The filter component for NOT filters.
118 * @param attributeType The attribute type for this filter.
119 * @param assertionValue The assertion value for this filter.
120 * @param subInitialElement The subInitial element for substring filters.
121 * @param subAnyElements The subAny elements for substring filters.
122 * @param subFinalElement The subFinal element for substring filters.
123 * @param matchingRuleID The matching rule ID for extensible filters.
124 * @param dnAttributes The dnAttributes flag for extensible filters.
125 */
126 public LDAPFilter(FilterType filterType,
127 ArrayList<RawFilter> filterComponents,
128 RawFilter notComponent, String attributeType,
129 ByteString assertionValue, ByteString subInitialElement,
130 ArrayList<ByteString> subAnyElements,
131 ByteString subFinalElement, String matchingRuleID,
132 boolean dnAttributes)
133 {
134 this.filterType = filterType;
135 this.filterComponents = filterComponents;
136 this.notComponent = notComponent;
137 this.attributeType = attributeType;
138 this.assertionValue = assertionValue;
139 this.subInitialElement = subInitialElement;
140 this.subAnyElements = subAnyElements;
141 this.subFinalElement = subFinalElement;
142 this.matchingRuleID = matchingRuleID;
143 this.dnAttributes = dnAttributes;
144 }
145
146
147
148 /**
149 * Creates a new LDAP filter from the provided search filter.
150 *
151 * @param filter The search filter to use to create this LDAP filter.
152 */
153 public LDAPFilter(SearchFilter filter)
154 {
155 this.filterType = filter.getFilterType();
156
157 switch (filterType)
158 {
159 case AND:
160 case OR:
161 Collection<SearchFilter> comps = filter.getFilterComponents();
162 filterComponents = new ArrayList<RawFilter>(comps.size());
163 for (SearchFilter f : comps)
164 {
165 filterComponents.add(new LDAPFilter(f));
166 }
167
168 notComponent = null;
169 attributeType = null;
170 assertionValue = null;
171 subInitialElement = null;
172 subAnyElements = null;
173 subFinalElement = null;
174 matchingRuleID = null;
175 dnAttributes = false;
176 break;
177 case NOT:
178 notComponent = new LDAPFilter(filter.getNotComponent());
179
180 filterComponents = null;
181 attributeType = null;
182 assertionValue = null;
183 subInitialElement = null;
184 subAnyElements = null;
185 subFinalElement = null;
186 matchingRuleID = null;
187 dnAttributes = false;
188 break;
189 case EQUALITY:
190 case GREATER_OR_EQUAL:
191 case LESS_OR_EQUAL:
192 case APPROXIMATE_MATCH:
193 attributeType = filter.getAttributeType().getNameOrOID();
194 assertionValue =
195 filter.getAssertionValue().getValue().toASN1OctetString();
196
197 filterComponents = null;
198 notComponent = null;
199 subInitialElement = null;
200 subAnyElements = null;
201 subFinalElement = null;
202 matchingRuleID = null;
203 dnAttributes = false;
204 break;
205 case SUBSTRING:
206 attributeType = filter.getAttributeType().getNameOrOID();
207
208 ByteString bs = filter.getSubInitialElement();
209 if (bs == null)
210 {
211 subInitialElement = null;
212 }
213 else
214 {
215 subInitialElement = bs.toASN1OctetString();
216 }
217
218 bs = filter.getSubFinalElement();
219 if (bs == null)
220 {
221 subFinalElement = null;
222 }
223 else
224 {
225 subFinalElement = bs.toASN1OctetString();
226 }
227
228 List<ByteString> subAnyStrings = filter.getSubAnyElements();
229 if (subAnyStrings == null)
230 {
231 subAnyElements = null;
232 }
233 else
234 {
235 subAnyElements = new ArrayList<ByteString>(subAnyStrings);
236 }
237
238 filterComponents = null;
239 notComponent = null;
240 assertionValue = null;
241 matchingRuleID = null;
242 dnAttributes = false;
243 break;
244 case PRESENT:
245 attributeType = filter.getAttributeType().getNameOrOID();
246
247 filterComponents = null;
248 notComponent = null;
249 assertionValue = null;
250 subInitialElement = null;
251 subAnyElements = null;
252 subFinalElement = null;
253 matchingRuleID = null;
254 dnAttributes = false;
255 break;
256 case EXTENSIBLE_MATCH:
257 dnAttributes = filter.getDNAttributes();
258 matchingRuleID = filter.getMatchingRuleID();
259
260 AttributeType attrType = filter.getAttributeType();
261 if (attrType == null)
262 {
263 attributeType = null;
264 }
265 else
266 {
267 attributeType = attrType.getNameOrOID();
268 }
269
270 AttributeValue av = filter.getAssertionValue();
271 if (av == null)
272 {
273 assertionValue = null;
274 }
275 else
276 {
277 assertionValue = av.getValue().toASN1OctetString();
278 }
279
280 filterComponents = null;
281 notComponent = null;
282 subInitialElement = null;
283 subAnyElements = null;
284 subFinalElement = null;
285 break;
286 }
287 }
288
289
290
291 /**
292 * Decodes the provided string into an LDAP search filter.
293 *
294 * @param filterString The string representation of the search filter to
295 * decode.
296 *
297 * @return The decoded LDAP search filter.
298 *
299 * @throws LDAPException If the provided string does not represent a valid
300 * LDAP search filter.
301 */
302 public static LDAPFilter decode(String filterString)
303 throws LDAPException
304 {
305 if (filterString == null)
306 {
307 Message message = ERR_LDAP_FILTER_STRING_NULL.get();
308 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
309 }
310
311
312 try
313 {
314 return decode(filterString, 0, filterString.length());
315 }
316 catch (LDAPException le)
317 {
318 if (debugEnabled())
319 {
320 TRACER.debugCaught(DebugLogLevel.ERROR, le);
321 }
322
323 throw le;
324 }
325 catch (Exception e)
326 {
327 if (debugEnabled())
328 {
329 TRACER.debugCaught(DebugLogLevel.ERROR, e);
330 }
331
332 Message message = ERR_LDAP_FILTER_UNCAUGHT_EXCEPTION.get(
333 filterString, String.valueOf(e));
334 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
335 }
336 }
337
338
339
340 /**
341 * Decodes the provided string into an LDAP search filter.
342 *
343 * @param filterString The string representation of the search filter to
344 * decode.
345 * @param startPos The position of the first character in the filter
346 * to parse.
347 * @param endPos The position of the first character after the end of
348 * the filter to parse.
349 *
350 * @return The decoded LDAP search filter.
351 *
352 * @throws LDAPException If the provided string does not represent a valid
353 * LDAP search filter.
354 */
355 private static LDAPFilter decode(String filterString, int startPos,
356 int endPos)
357 throws LDAPException
358 {
359 // Make sure that the length is sufficient for a valid search filter.
360 int length = endPos - startPos;
361 if (length <= 0)
362 {
363 Message message = ERR_LDAP_FILTER_STRING_NULL.get();
364 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
365 }
366
367 // If the filter is enclosed in a pair of apostrophes ("single-quotes") it
368 // is invalid (issue #1024).
369 if (1 < filterString.length()
370 && filterString.startsWith("'") && filterString.endsWith("'"))
371 {
372 Message message =
373 ERR_LDAP_FILTER_ENCLOSED_IN_APOSTROPHES.get(filterString);
374 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
375 }
376
377 // If the filter is surrounded by parentheses (which it should be), then
378 // strip them off.
379 if (filterString.charAt(startPos) == '(')
380 {
381 if (filterString.charAt(endPos-1) == ')')
382 {
383 startPos++;
384 endPos--;
385 }
386 else
387 {
388 Message message = ERR_LDAP_FILTER_MISMATCHED_PARENTHESES.get(
389 filterString, startPos, endPos);
390 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
391 }
392 }
393
394
395 // Look at the first character. If it is a '&' then it is an AND search.
396 // If it is a '|' then it is an OR search. If it is a '!' then it is a NOT
397 // search.
398 char c = filterString.charAt(startPos);
399 if (c == '&')
400 {
401 return decodeCompoundFilter(FilterType.AND, filterString, startPos+1,
402 endPos);
403 }
404 else if (c == '|')
405 {
406 return decodeCompoundFilter(FilterType.OR, filterString, startPos+1,
407 endPos);
408 }
409 else if (c == '!')
410 {
411 return decodeCompoundFilter(FilterType.NOT, filterString, startPos+1,
412 endPos);
413 }
414
415
416 // If we've gotten here, then it must be a simple filter. It must have an
417 // equal sign at some point, so find it.
418 int equalPos = -1;
419 for (int i=startPos; i < endPos; i++)
420 {
421 if (filterString.charAt(i) == '=')
422 {
423 equalPos = i;
424 break;
425 }
426 }
427
428 if (equalPos <= startPos)
429 {
430 Message message =
431 ERR_LDAP_FILTER_NO_EQUAL_SIGN.get(filterString, startPos, endPos);
432 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
433 }
434
435
436 // Look at the character immediately before the equal sign, because it may
437 // help determine the filter type.
438 int attrEndPos;
439 FilterType filterType;
440 switch (filterString.charAt(equalPos-1))
441 {
442 case '~':
443 filterType = FilterType.APPROXIMATE_MATCH;
444 attrEndPos = equalPos-1;
445 break;
446 case '>':
447 filterType = FilterType.GREATER_OR_EQUAL;
448 attrEndPos = equalPos-1;
449 break;
450 case '<':
451 filterType = FilterType.LESS_OR_EQUAL;
452 attrEndPos = equalPos-1;
453 break;
454 case ':':
455 return decodeExtensibleMatchFilter(filterString, startPos, equalPos,
456 endPos);
457 default:
458 filterType = FilterType.EQUALITY;
459 attrEndPos = equalPos;
460 break;
461 }
462
463
464 // The part of the filter string before the equal sign should be the
465 // attribute type. Make sure that the characters it contains are acceptable
466 // for attribute types, including those allowed by attribute name
467 // exceptions (ASCII letters and digits, the dash, and the underscore). We
468 // also need to allow attribute options, which includes the semicolon and
469 // the equal sign.
470 String attrType = filterString.substring(startPos, attrEndPos);
471 for (int i=0; i < attrType.length(); i++)
472 {
473 switch (attrType.charAt(i))
474 {
475 case '-':
476 case '0':
477 case '1':
478 case '2':
479 case '3':
480 case '4':
481 case '5':
482 case '6':
483 case '7':
484 case '8':
485 case '9':
486 case ';':
487 case '=':
488 case 'A':
489 case 'B':
490 case 'C':
491 case 'D':
492 case 'E':
493 case 'F':
494 case 'G':
495 case 'H':
496 case 'I':
497 case 'J':
498 case 'K':
499 case 'L':
500 case 'M':
501 case 'N':
502 case 'O':
503 case 'P':
504 case 'Q':
505 case 'R':
506 case 'S':
507 case 'T':
508 case 'U':
509 case 'V':
510 case 'W':
511 case 'X':
512 case 'Y':
513 case 'Z':
514 case '_':
515 case 'a':
516 case 'b':
517 case 'c':
518 case 'd':
519 case 'e':
520 case 'f':
521 case 'g':
522 case 'h':
523 case 'i':
524 case 'j':
525 case 'k':
526 case 'l':
527 case 'm':
528 case 'n':
529 case 'o':
530 case 'p':
531 case 'q':
532 case 'r':
533 case 's':
534 case 't':
535 case 'u':
536 case 'v':
537 case 'w':
538 case 'x':
539 case 'y':
540 case 'z':
541 // These are all OK.
542 break;
543
544 case '.':
545 case '/':
546 case ':':
547 case '<':
548 case '>':
549 case '?':
550 case '@':
551 case '[':
552 case '\\':
553 case ']':
554 case '^':
555 case '`':
556 // These are not allowed, but they are explicitly called out because
557 // they are included in the range of values between '-' and 'z', and
558 // making sure all possible characters are included can help make the
559 // switch statement more efficient. We'll fall through to the default
560 // clause to reject them.
561 default:
562 Message message = ERR_LDAP_FILTER_INVALID_CHAR_IN_ATTR_TYPE.get(
563 attrType, String.valueOf(attrType.charAt(i)), i);
564 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
565 }
566 }
567
568
569 // Get the attribute value.
570 String valueStr = filterString.substring(equalPos+1, endPos);
571 if (valueStr.length() == 0)
572 {
573 return new LDAPFilter(filterType, null, null, attrType,
574 new ASN1OctetString(), null, null, null, null,
575 false);
576 }
577 else if (valueStr.equals("*"))
578 {
579 return new LDAPFilter(FilterType.PRESENT, null, null, attrType, null,
580 null, null, null, null, false);
581 }
582 else if (valueStr.indexOf('*') >= 0)
583 {
584 return decodeSubstringFilter(filterString, attrType, equalPos, endPos);
585 }
586 else
587 {
588 boolean hasEscape = false;
589 byte[] valueBytes = getBytes(valueStr);
590 for (int i=0; i < valueBytes.length; i++)
591 {
592 if (valueBytes[i] == 0x5C) // The backslash character
593 {
594 hasEscape = true;
595 break;
596 }
597 }
598
599 ASN1OctetString value;
600 if (hasEscape)
601 {
602 ByteBuffer valueBuffer = ByteBuffer.allocate(valueStr.length());
603 for (int i=0; i < valueBytes.length; i++)
604 {
605 if (valueBytes[i] == 0x5C) // The backslash character
606 {
607 // The next two bytes must be the hex characters that comprise the
608 // binary value.
609 if ((i + 2) >= valueBytes.length)
610 {
611 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
612 filterString, equalPos+i+1);
613 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
614 }
615
616 byte byteValue = 0;
617 switch (valueBytes[++i])
618 {
619 case 0x30: // '0'
620 break;
621 case 0x31: // '1'
622 byteValue = (byte) 0x10;
623 break;
624 case 0x32: // '2'
625 byteValue = (byte) 0x20;
626 break;
627 case 0x33: // '3'
628 byteValue = (byte) 0x30;
629 break;
630 case 0x34: // '4'
631 byteValue = (byte) 0x40;
632 break;
633 case 0x35: // '5'
634 byteValue = (byte) 0x50;
635 break;
636 case 0x36: // '6'
637 byteValue = (byte) 0x60;
638 break;
639 case 0x37: // '7'
640 byteValue = (byte) 0x70;
641 break;
642 case 0x38: // '8'
643 byteValue = (byte) 0x80;
644 break;
645 case 0x39: // '9'
646 byteValue = (byte) 0x90;
647 break;
648 case 0x41: // 'A'
649 case 0x61: // 'a'
650 byteValue = (byte) 0xA0;
651 break;
652 case 0x42: // 'B'
653 case 0x62: // 'b'
654 byteValue = (byte) 0xB0;
655 break;
656 case 0x43: // 'C'
657 case 0x63: // 'c'
658 byteValue = (byte) 0xC0;
659 break;
660 case 0x44: // 'D'
661 case 0x64: // 'd'
662 byteValue = (byte) 0xD0;
663 break;
664 case 0x45: // 'E'
665 case 0x65: // 'e'
666 byteValue = (byte) 0xE0;
667 break;
668 case 0x46: // 'F'
669 case 0x66: // 'f'
670 byteValue = (byte) 0xF0;
671 break;
672 default:
673 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
674 filterString, equalPos+i+1);
675 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
676 }
677
678 switch (valueBytes[++i])
679 {
680 case 0x30: // '0'
681 break;
682 case 0x31: // '1'
683 byteValue |= (byte) 0x01;
684 break;
685 case 0x32: // '2'
686 byteValue |= (byte) 0x02;
687 break;
688 case 0x33: // '3'
689 byteValue |= (byte) 0x03;
690 break;
691 case 0x34: // '4'
692 byteValue |= (byte) 0x04;
693 break;
694 case 0x35: // '5'
695 byteValue |= (byte) 0x05;
696 break;
697 case 0x36: // '6'
698 byteValue |= (byte) 0x06;
699 break;
700 case 0x37: // '7'
701 byteValue |= (byte) 0x07;
702 break;
703 case 0x38: // '8'
704 byteValue |= (byte) 0x08;
705 break;
706 case 0x39: // '9'
707 byteValue |= (byte) 0x09;
708 break;
709 case 0x41: // 'A'
710 case 0x61: // 'a'
711 byteValue |= (byte) 0x0A;
712 break;
713 case 0x42: // 'B'
714 case 0x62: // 'b'
715 byteValue |= (byte) 0x0B;
716 break;
717 case 0x43: // 'C'
718 case 0x63: // 'c'
719 byteValue |= (byte) 0x0C;
720 break;
721 case 0x44: // 'D'
722 case 0x64: // 'd'
723 byteValue |= (byte) 0x0D;
724 break;
725 case 0x45: // 'E'
726 case 0x65: // 'e'
727 byteValue |= (byte) 0x0E;
728 break;
729 case 0x46: // 'F'
730 case 0x66: // 'f'
731 byteValue |= (byte) 0x0F;
732 break;
733 default:
734 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
735 filterString, equalPos+i+1);
736 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
737 }
738
739 valueBuffer.put(byteValue);
740 }
741 else
742 {
743 valueBuffer.put(valueBytes[i]);
744 }
745 }
746
747 valueBytes = new byte[valueBuffer.position()];
748 valueBuffer.flip();
749 valueBuffer.get(valueBytes);
750 value = new ASN1OctetString(valueBytes);
751 }
752 else
753 {
754 value = new ASN1OctetString(valueBytes);
755 }
756
757 return new LDAPFilter(filterType, null, null, attrType, value, null, null,
758 null, null, false);
759 }
760 }
761
762
763
764 /**
765 * Decodes a set of filters from the provided filter string within the
766 * indicated range.
767 *
768 * @param filterType The filter type for this compound filter. It must be
769 * an AND, OR or NOT filter.
770 * @param filterString The string containing the filter information to
771 * decode.
772 * @param startPos The position of the first character in the set of
773 * filters to decode.
774 * @param endPos The position of the first character after the end of
775 * the set of filters to decode.
776 *
777 * @return The decoded LDAP filter.
778 *
779 * @throws LDAPException If a problem occurs while attempting to decode the
780 * compound filter.
781 */
782 private static LDAPFilter decodeCompoundFilter(FilterType filterType,
783 String filterString,
784 int startPos, int endPos)
785 throws LDAPException
786 {
787 // Create a list to hold the returned components.
788 ArrayList<RawFilter> filterComponents = new ArrayList<RawFilter>();
789
790
791 // If the end pos is equal to the start pos, then there are no components.
792 if (startPos == endPos)
793 {
794 if (filterType == FilterType.NOT)
795 {
796 Message message =
797 ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos);
798 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
799 }
800 else
801 {
802 // This is valid and will be treated as a TRUE/FALSE filter.
803 return new LDAPFilter(filterType, filterComponents, null, null, null,
804 null, null, null, null, false);
805 }
806 }
807
808
809 // The first and last characters must be parentheses. If not, then that's
810 // an error.
811 if ((filterString.charAt(startPos) != '(') ||
812 (filterString.charAt(endPos-1) != ')'))
813 {
814 Message message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get(
815 filterString, startPos, endPos);
816 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
817 }
818
819
820 // Iterate through the characters in the value. Whenever an open
821 // parenthesis is found, locate the corresponding close parenthesis by
822 // counting the number of intermediate open/close parentheses.
823 int pendingOpens = 0;
824 int openPos = -1;
825 for (int i=startPos; i < endPos; i++)
826 {
827 char c = filterString.charAt(i);
828 if (c == '(')
829 {
830 if (openPos < 0)
831 {
832 openPos = i;
833 }
834
835 pendingOpens++;
836 }
837 else if (c == ')')
838 {
839 pendingOpens--;
840 if (pendingOpens == 0)
841 {
842 filterComponents.add(decode(filterString, openPos, i+1));
843 openPos = -1;
844 }
845 else if (pendingOpens < 0)
846 {
847 Message message = ERR_LDAP_FILTER_NO_CORRESPONDING_OPEN_PARENTHESIS.
848 get(filterString, i);
849 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
850 }
851 }
852 else if (pendingOpens <= 0)
853 {
854 Message message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get(
855 filterString, startPos, endPos);
856 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
857 }
858 }
859
860
861 // At this point, we have parsed the entire set of filter components. The
862 // list of open parenthesis positions must be empty.
863 if (pendingOpens != 0)
864 {
865 Message message = ERR_LDAP_FILTER_NO_CORRESPONDING_CLOSE_PARENTHESIS.get(
866 filterString, openPos);
867 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
868 }
869
870
871 // We should have everything we need, so return the list.
872 if (filterType == FilterType.NOT)
873 {
874 if (filterComponents.size() != 1)
875 {
876 Message message =
877 ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos);
878 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
879 }
880 RawFilter notComponent = filterComponents.get(0);
881 return new LDAPFilter(filterType, null, notComponent, null, null,
882 null, null, null, null, false);
883 }
884 else
885 {
886 return new LDAPFilter(filterType, filterComponents, null, null, null,
887 null, null, null, null, false);
888 }
889 }
890
891
892
893 /**
894 * Decodes a substring search filter component based on the provided
895 * information.
896 *
897 * @param filterString The filter string containing the information to
898 * decode.
899 * @param attrType The attribute type for this substring filter
900 * component.
901 * @param equalPos The location of the equal sign separating the
902 * attribute type from the value.
903 * @param endPos The position of the first character after the end of
904 * the substring value.
905 *
906 * @return The decoded LDAP filter.
907 *
908 * @throws LDAPException If a problem occurs while attempting to decode the
909 * substring filter.
910 */
911 private static LDAPFilter decodeSubstringFilter(String filterString,
912 String attrType, int equalPos,
913 int endPos)
914 throws LDAPException
915 {
916 // Get a binary representation of the value.
917 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos));
918
919
920 // Find the locations of all the asterisks in the value. Also, check to
921 // see if there are any escaped values, since they will need special
922 // treatment.
923 boolean hasEscape = false;
924 LinkedList<Integer> asteriskPositions = new LinkedList<Integer>();
925 for (int i=0; i < valueBytes.length; i++)
926 {
927 if (valueBytes[i] == 0x2A) // The asterisk.
928 {
929 asteriskPositions.add(i);
930 }
931 else if (valueBytes[i] == 0x5C) // The backslash.
932 {
933 hasEscape = true;
934 }
935 }
936
937
938 // If there were no asterisks, then this isn't a substring filter.
939 if (asteriskPositions.isEmpty())
940 {
941 Message message = ERR_LDAP_FILTER_SUBSTRING_NO_ASTERISKS.get(
942 filterString, equalPos+1, endPos);
943 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
944 }
945
946
947 // If the value starts with an asterisk, then there is no subInitial
948 // component. Otherwise, parse out the subInitial.
949 ByteString subInitial;
950 int firstPos = asteriskPositions.removeFirst();
951 if (firstPos == 0)
952 {
953 subInitial = null;
954 }
955 else
956 {
957 if (hasEscape)
958 {
959 ByteBuffer buffer = ByteBuffer.allocate(firstPos);
960 for (int i=0; i < firstPos; i++)
961 {
962 if (valueBytes[i] == 0x5C)
963 {
964 // The next two bytes must be the hex characters that comprise the
965 // binary value.
966 if ((i + 2) >= valueBytes.length)
967 {
968 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
969 filterString, equalPos+i+1);
970 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
971 }
972
973 byte byteValue = 0;
974 switch (valueBytes[++i])
975 {
976 case 0x30: // '0'
977 break;
978 case 0x31: // '1'
979 byteValue = (byte) 0x10;
980 break;
981 case 0x32: // '2'
982 byteValue = (byte) 0x20;
983 break;
984 case 0x33: // '3'
985 byteValue = (byte) 0x30;
986 break;
987 case 0x34: // '4'
988 byteValue = (byte) 0x40;
989 break;
990 case 0x35: // '5'
991 byteValue = (byte) 0x50;
992 break;
993 case 0x36: // '6'
994 byteValue = (byte) 0x60;
995 break;
996 case 0x37: // '7'
997 byteValue = (byte) 0x70;
998 break;
999 case 0x38: // '8'
1000 byteValue = (byte) 0x80;
1001 break;
1002 case 0x39: // '9'
1003 byteValue = (byte) 0x90;
1004 break;
1005 case 0x41: // 'A'
1006 case 0x61: // 'a'
1007 byteValue = (byte) 0xA0;
1008 break;
1009 case 0x42: // 'B'
1010 case 0x62: // 'b'
1011 byteValue = (byte) 0xB0;
1012 break;
1013 case 0x43: // 'C'
1014 case 0x63: // 'c'
1015 byteValue = (byte) 0xC0;
1016 break;
1017 case 0x44: // 'D'
1018 case 0x64: // 'd'
1019 byteValue = (byte) 0xD0;
1020 break;
1021 case 0x45: // 'E'
1022 case 0x65: // 'e'
1023 byteValue = (byte) 0xE0;
1024 break;
1025 case 0x46: // 'F'
1026 case 0x66: // 'f'
1027 byteValue = (byte) 0xF0;
1028 break;
1029 default:
1030 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1031 filterString, equalPos+i+1);
1032 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1033 }
1034
1035 switch (valueBytes[++i])
1036 {
1037 case 0x30: // '0'
1038 break;
1039 case 0x31: // '1'
1040 byteValue |= (byte) 0x01;
1041 break;
1042 case 0x32: // '2'
1043 byteValue |= (byte) 0x02;
1044 break;
1045 case 0x33: // '3'
1046 byteValue |= (byte) 0x03;
1047 break;
1048 case 0x34: // '4'
1049 byteValue |= (byte) 0x04;
1050 break;
1051 case 0x35: // '5'
1052 byteValue |= (byte) 0x05;
1053 break;
1054 case 0x36: // '6'
1055 byteValue |= (byte) 0x06;
1056 break;
1057 case 0x37: // '7'
1058 byteValue |= (byte) 0x07;
1059 break;
1060 case 0x38: // '8'
1061 byteValue |= (byte) 0x08;
1062 break;
1063 case 0x39: // '9'
1064 byteValue |= (byte) 0x09;
1065 break;
1066 case 0x41: // 'A'
1067 case 0x61: // 'a'
1068 byteValue |= (byte) 0x0A;
1069 break;
1070 case 0x42: // 'B'
1071 case 0x62: // 'b'
1072 byteValue |= (byte) 0x0B;
1073 break;
1074 case 0x43: // 'C'
1075 case 0x63: // 'c'
1076 byteValue |= (byte) 0x0C;
1077 break;
1078 case 0x44: // 'D'
1079 case 0x64: // 'd'
1080 byteValue |= (byte) 0x0D;
1081 break;
1082 case 0x45: // 'E'
1083 case 0x65: // 'e'
1084 byteValue |= (byte) 0x0E;
1085 break;
1086 case 0x46: // 'F'
1087 case 0x66: // 'f'
1088 byteValue |= (byte) 0x0F;
1089 break;
1090 default:
1091 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1092 filterString, equalPos+i+1);
1093 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1094 }
1095
1096 buffer.put(byteValue);
1097 }
1098 else
1099 {
1100 buffer.put(valueBytes[i]);
1101 }
1102 }
1103
1104 byte[] subInitialBytes = new byte[buffer.position()];
1105 buffer.flip();
1106 buffer.get(subInitialBytes);
1107 subInitial = new ASN1OctetString(subInitialBytes);
1108 }
1109 else
1110 {
1111 byte[] subInitialBytes = new byte[firstPos];
1112 System.arraycopy(valueBytes, 0, subInitialBytes, 0, firstPos);
1113 subInitial = new ASN1OctetString(subInitialBytes);
1114 }
1115 }
1116
1117
1118 // Next, process through the rest of the asterisks to get the subAny values.
1119 ArrayList<ByteString> subAny = new ArrayList<ByteString>();
1120 for (int asteriskPos : asteriskPositions)
1121 {
1122 int length = asteriskPos - firstPos - 1;
1123
1124 if (hasEscape)
1125 {
1126 ByteBuffer buffer = ByteBuffer.allocate(length);
1127 for (int i=firstPos+1; i < asteriskPos; i++)
1128 {
1129 if (valueBytes[i] == 0x5C)
1130 {
1131 // The next two bytes must be the hex characters that comprise the
1132 // binary value.
1133 if ((i + 2) >= valueBytes.length)
1134 {
1135 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1136 filterString, equalPos+i+1);
1137 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1138 }
1139
1140 byte byteValue = 0;
1141 switch (valueBytes[++i])
1142 {
1143 case 0x30: // '0'
1144 break;
1145 case 0x31: // '1'
1146 byteValue = (byte) 0x10;
1147 break;
1148 case 0x32: // '2'
1149 byteValue = (byte) 0x20;
1150 break;
1151 case 0x33: // '3'
1152 byteValue = (byte) 0x30;
1153 break;
1154 case 0x34: // '4'
1155 byteValue = (byte) 0x40;
1156 break;
1157 case 0x35: // '5'
1158 byteValue = (byte) 0x50;
1159 break;
1160 case 0x36: // '6'
1161 byteValue = (byte) 0x60;
1162 break;
1163 case 0x37: // '7'
1164 byteValue = (byte) 0x70;
1165 break;
1166 case 0x38: // '8'
1167 byteValue = (byte) 0x80;
1168 break;
1169 case 0x39: // '9'
1170 byteValue = (byte) 0x90;
1171 break;
1172 case 0x41: // 'A'
1173 case 0x61: // 'a'
1174 byteValue = (byte) 0xA0;
1175 break;
1176 case 0x42: // 'B'
1177 case 0x62: // 'b'
1178 byteValue = (byte) 0xB0;
1179 break;
1180 case 0x43: // 'C'
1181 case 0x63: // 'c'
1182 byteValue = (byte) 0xC0;
1183 break;
1184 case 0x44: // 'D'
1185 case 0x64: // 'd'
1186 byteValue = (byte) 0xD0;
1187 break;
1188 case 0x45: // 'E'
1189 case 0x65: // 'e'
1190 byteValue = (byte) 0xE0;
1191 break;
1192 case 0x46: // 'F'
1193 case 0x66: // 'f'
1194 byteValue = (byte) 0xF0;
1195 break;
1196 default:
1197 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1198 filterString, equalPos+i+1);
1199 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1200 }
1201
1202 switch (valueBytes[++i])
1203 {
1204 case 0x30: // '0'
1205 break;
1206 case 0x31: // '1'
1207 byteValue |= (byte) 0x01;
1208 break;
1209 case 0x32: // '2'
1210 byteValue |= (byte) 0x02;
1211 break;
1212 case 0x33: // '3'
1213 byteValue |= (byte) 0x03;
1214 break;
1215 case 0x34: // '4'
1216 byteValue |= (byte) 0x04;
1217 break;
1218 case 0x35: // '5'
1219 byteValue |= (byte) 0x05;
1220 break;
1221 case 0x36: // '6'
1222 byteValue |= (byte) 0x06;
1223 break;
1224 case 0x37: // '7'
1225 byteValue |= (byte) 0x07;
1226 break;
1227 case 0x38: // '8'
1228 byteValue |= (byte) 0x08;
1229 break;
1230 case 0x39: // '9'
1231 byteValue |= (byte) 0x09;
1232 break;
1233 case 0x41: // 'A'
1234 case 0x61: // 'a'
1235 byteValue |= (byte) 0x0A;
1236 break;
1237 case 0x42: // 'B'
1238 case 0x62: // 'b'
1239 byteValue |= (byte) 0x0B;
1240 break;
1241 case 0x43: // 'C'
1242 case 0x63: // 'c'
1243 byteValue |= (byte) 0x0C;
1244 break;
1245 case 0x44: // 'D'
1246 case 0x64: // 'd'
1247 byteValue |= (byte) 0x0D;
1248 break;
1249 case 0x45: // 'E'
1250 case 0x65: // 'e'
1251 byteValue |= (byte) 0x0E;
1252 break;
1253 case 0x46: // 'F'
1254 case 0x66: // 'f'
1255 byteValue |= (byte) 0x0F;
1256 break;
1257 default:
1258 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1259 filterString, equalPos+i+1);
1260 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1261 }
1262
1263 buffer.put(byteValue);
1264 }
1265 else
1266 {
1267 buffer.put(valueBytes[i]);
1268 }
1269 }
1270
1271 byte[] subAnyBytes = new byte[buffer.position()];
1272 buffer.flip();
1273 buffer.get(subAnyBytes);
1274 subAny.add(new ASN1OctetString(subAnyBytes));
1275 }
1276 else
1277 {
1278 byte[] subAnyBytes = new byte[length];
1279 System.arraycopy(valueBytes, firstPos+1, subAnyBytes, 0, length);
1280 subAny.add(new ASN1OctetString(subAnyBytes));
1281 }
1282
1283
1284 firstPos = asteriskPos;
1285 }
1286
1287
1288 // Finally, see if there is anything after the last asterisk, which would be
1289 // the subFinal value.
1290 ByteString subFinal;
1291 if (firstPos == (valueBytes.length-1))
1292 {
1293 subFinal = null;
1294 }
1295 else
1296 {
1297 int length = valueBytes.length - firstPos - 1;
1298
1299 if (hasEscape)
1300 {
1301 ByteBuffer buffer = ByteBuffer.allocate(length);
1302 for (int i=firstPos+1; i < valueBytes.length; i++)
1303 {
1304 if (valueBytes[i] == 0x5C)
1305 {
1306 // The next two bytes must be the hex characters that comprise the
1307 // binary value.
1308 if ((i + 2) >= valueBytes.length)
1309 {
1310 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1311 filterString, equalPos+i+1);
1312 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1313 }
1314
1315 byte byteValue = 0;
1316 switch (valueBytes[++i])
1317 {
1318 case 0x30: // '0'
1319 break;
1320 case 0x31: // '1'
1321 byteValue = (byte) 0x10;
1322 break;
1323 case 0x32: // '2'
1324 byteValue = (byte) 0x20;
1325 break;
1326 case 0x33: // '3'
1327 byteValue = (byte) 0x30;
1328 break;
1329 case 0x34: // '4'
1330 byteValue = (byte) 0x40;
1331 break;
1332 case 0x35: // '5'
1333 byteValue = (byte) 0x50;
1334 break;
1335 case 0x36: // '6'
1336 byteValue = (byte) 0x60;
1337 break;
1338 case 0x37: // '7'
1339 byteValue = (byte) 0x70;
1340 break;
1341 case 0x38: // '8'
1342 byteValue = (byte) 0x80;
1343 break;
1344 case 0x39: // '9'
1345 byteValue = (byte) 0x90;
1346 break;
1347 case 0x41: // 'A'
1348 case 0x61: // 'a'
1349 byteValue = (byte) 0xA0;
1350 break;
1351 case 0x42: // 'B'
1352 case 0x62: // 'b'
1353 byteValue = (byte) 0xB0;
1354 break;
1355 case 0x43: // 'C'
1356 case 0x63: // 'c'
1357 byteValue = (byte) 0xC0;
1358 break;
1359 case 0x44: // 'D'
1360 case 0x64: // 'd'
1361 byteValue = (byte) 0xD0;
1362 break;
1363 case 0x45: // 'E'
1364 case 0x65: // 'e'
1365 byteValue = (byte) 0xE0;
1366 break;
1367 case 0x46: // 'F'
1368 case 0x66: // 'f'
1369 byteValue = (byte) 0xF0;
1370 break;
1371 default:
1372 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1373 filterString, equalPos+i+1);
1374 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1375 }
1376
1377 switch (valueBytes[++i])
1378 {
1379 case 0x30: // '0'
1380 break;
1381 case 0x31: // '1'
1382 byteValue |= (byte) 0x01;
1383 break;
1384 case 0x32: // '2'
1385 byteValue |= (byte) 0x02;
1386 break;
1387 case 0x33: // '3'
1388 byteValue |= (byte) 0x03;
1389 break;
1390 case 0x34: // '4'
1391 byteValue |= (byte) 0x04;
1392 break;
1393 case 0x35: // '5'
1394 byteValue |= (byte) 0x05;
1395 break;
1396 case 0x36: // '6'
1397 byteValue |= (byte) 0x06;
1398 break;
1399 case 0x37: // '7'
1400 byteValue |= (byte) 0x07;
1401 break;
1402 case 0x38: // '8'
1403 byteValue |= (byte) 0x08;
1404 break;
1405 case 0x39: // '9'
1406 byteValue |= (byte) 0x09;
1407 break;
1408 case 0x41: // 'A'
1409 case 0x61: // 'a'
1410 byteValue |= (byte) 0x0A;
1411 break;
1412 case 0x42: // 'B'
1413 case 0x62: // 'b'
1414 byteValue |= (byte) 0x0B;
1415 break;
1416 case 0x43: // 'C'
1417 case 0x63: // 'c'
1418 byteValue |= (byte) 0x0C;
1419 break;
1420 case 0x44: // 'D'
1421 case 0x64: // 'd'
1422 byteValue |= (byte) 0x0D;
1423 break;
1424 case 0x45: // 'E'
1425 case 0x65: // 'e'
1426 byteValue |= (byte) 0x0E;
1427 break;
1428 case 0x46: // 'F'
1429 case 0x66: // 'f'
1430 byteValue |= (byte) 0x0F;
1431 break;
1432 default:
1433 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1434 filterString, equalPos+i+1);
1435 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1436 }
1437
1438 buffer.put(byteValue);
1439 }
1440 else
1441 {
1442 buffer.put(valueBytes[i]);
1443 }
1444 }
1445
1446 byte[] subFinalBytes = new byte[buffer.position()];
1447 buffer.flip();
1448 buffer.get(subFinalBytes);
1449 subFinal = new ASN1OctetString(subFinalBytes);
1450 }
1451 else
1452 {
1453 byte[] subFinalBytes = new byte[length];
1454 System.arraycopy(valueBytes, firstPos+1, subFinalBytes, 0, length);
1455 subFinal = new ASN1OctetString(subFinalBytes);
1456 }
1457 }
1458
1459
1460 return new LDAPFilter(FilterType.SUBSTRING, null, null, attrType, null,
1461 subInitial, subAny, subFinal, null, false);
1462 }
1463
1464
1465
1466 /**
1467 * Decodes an extensible match filter component based on the provided
1468 * information.
1469 *
1470 * @param filterString The filter string containing the information to
1471 * decode.
1472 * @param startPos The position in the filter string of the first
1473 * character in the extensible match filter.
1474 * @param equalPos The position of the equal sign in the extensible
1475 * match filter.
1476 * @param endPos The position of the first character after the end of
1477 * the extensible match filter.
1478 *
1479 * @return The decoded LDAP filter.
1480 *
1481 * @throws LDAPException If a problem occurs while attempting to decode the
1482 * extensible match filter.
1483 */
1484 private static LDAPFilter decodeExtensibleMatchFilter(String filterString,
1485 int startPos,
1486 int equalPos,
1487 int endPos)
1488 throws LDAPException
1489 {
1490 String attributeType = null;
1491 boolean dnAttributes = false;
1492 String matchingRuleID = null;
1493
1494
1495 // Look at the first character. If it is a colon, then it must be followed
1496 // by either the string "dn" or the matching rule ID. If it is not, then
1497 // must be the attribute type.
1498 String lowerLeftStr =
1499 toLowerCase(filterString.substring(startPos, equalPos));
1500 if (filterString.charAt(startPos) == ':')
1501 {
1502 // See if it starts with ":dn". Otherwise, it much be the matching rule
1503 // ID.
1504 if (lowerLeftStr.startsWith(":dn:"))
1505 {
1506 dnAttributes = true;
1507
1508 if((startPos+4) < (equalPos-1))
1509 {
1510 matchingRuleID = filterString.substring(startPos+4, equalPos-1);
1511 }
1512 }
1513 else
1514 {
1515 matchingRuleID = filterString.substring(startPos+1, equalPos-1);
1516 }
1517 }
1518 else
1519 {
1520 int colonPos = filterString.indexOf(':',startPos);
1521 if (colonPos < 0)
1522 {
1523 Message message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_COLON.get(
1524 filterString, startPos);
1525 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1526 }
1527
1528
1529 attributeType = filterString.substring(startPos, colonPos);
1530
1531
1532 // If there is anything left, then it should be ":dn" and/or ":" followed
1533 // by the matching rule ID.
1534 if (colonPos < (equalPos-1))
1535 {
1536 if (lowerLeftStr.startsWith(":dn:", colonPos - startPos))
1537 {
1538 dnAttributes = true;
1539
1540 if ((colonPos+4) < (equalPos-1))
1541 {
1542 matchingRuleID = filterString.substring(colonPos+4, equalPos-1);
1543 }
1544 }
1545 else
1546 {
1547 matchingRuleID = filterString.substring(colonPos+1, equalPos-1);
1548 }
1549 }
1550 }
1551
1552
1553 // Parse out the attribute value.
1554 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos));
1555 boolean hasEscape = false;
1556 for (int i=0; i < valueBytes.length; i++)
1557 {
1558 if (valueBytes[i] == 0x5C)
1559 {
1560 hasEscape = true;
1561 break;
1562 }
1563 }
1564
1565 ByteString value;
1566 if (hasEscape)
1567 {
1568 ByteBuffer valueBuffer = ByteBuffer.allocate(valueBytes.length);
1569 for (int i=0; i < valueBytes.length; i++)
1570 {
1571 if (valueBytes[i] == 0x5C) // The backslash character
1572 {
1573 // The next two bytes must be the hex characters that comprise the
1574 // binary value.
1575 if ((i + 2) >= valueBytes.length)
1576 {
1577 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1578 filterString, equalPos+i+1);
1579 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1580 }
1581
1582 byte byteValue = 0;
1583 switch (valueBytes[++i])
1584 {
1585 case 0x30: // '0'
1586 break;
1587 case 0x31: // '1'
1588 byteValue = (byte) 0x10;
1589 break;
1590 case 0x32: // '2'
1591 byteValue = (byte) 0x20;
1592 break;
1593 case 0x33: // '3'
1594 byteValue = (byte) 0x30;
1595 break;
1596 case 0x34: // '4'
1597 byteValue = (byte) 0x40;
1598 break;
1599 case 0x35: // '5'
1600 byteValue = (byte) 0x50;
1601 break;
1602 case 0x36: // '6'
1603 byteValue = (byte) 0x60;
1604 break;
1605 case 0x37: // '7'
1606 byteValue = (byte) 0x70;
1607 break;
1608 case 0x38: // '8'
1609 byteValue = (byte) 0x80;
1610 break;
1611 case 0x39: // '9'
1612 byteValue = (byte) 0x90;
1613 break;
1614 case 0x41: // 'A'
1615 case 0x61: // 'a'
1616 byteValue = (byte) 0xA0;
1617 break;
1618 case 0x42: // 'B'
1619 case 0x62: // 'b'
1620 byteValue = (byte) 0xB0;
1621 break;
1622 case 0x43: // 'C'
1623 case 0x63: // 'c'
1624 byteValue = (byte) 0xC0;
1625 break;
1626 case 0x44: // 'D'
1627 case 0x64: // 'd'
1628 byteValue = (byte) 0xD0;
1629 break;
1630 case 0x45: // 'E'
1631 case 0x65: // 'e'
1632 byteValue = (byte) 0xE0;
1633 break;
1634 case 0x46: // 'F'
1635 case 0x66: // 'f'
1636 byteValue = (byte) 0xF0;
1637 break;
1638 default:
1639 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1640 filterString, equalPos+i+1);
1641 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1642 }
1643
1644 switch (valueBytes[++i])
1645 {
1646 case 0x30: // '0'
1647 break;
1648 case 0x31: // '1'
1649 byteValue |= (byte) 0x01;
1650 break;
1651 case 0x32: // '2'
1652 byteValue |= (byte) 0x02;
1653 break;
1654 case 0x33: // '3'
1655 byteValue |= (byte) 0x03;
1656 break;
1657 case 0x34: // '4'
1658 byteValue |= (byte) 0x04;
1659 break;
1660 case 0x35: // '5'
1661 byteValue |= (byte) 0x05;
1662 break;
1663 case 0x36: // '6'
1664 byteValue |= (byte) 0x06;
1665 break;
1666 case 0x37: // '7'
1667 byteValue |= (byte) 0x07;
1668 break;
1669 case 0x38: // '8'
1670 byteValue |= (byte) 0x08;
1671 break;
1672 case 0x39: // '9'
1673 byteValue |= (byte) 0x09;
1674 break;
1675 case 0x41: // 'A'
1676 case 0x61: // 'a'
1677 byteValue |= (byte) 0x0A;
1678 break;
1679 case 0x42: // 'B'
1680 case 0x62: // 'b'
1681 byteValue |= (byte) 0x0B;
1682 break;
1683 case 0x43: // 'C'
1684 case 0x63: // 'c'
1685 byteValue |= (byte) 0x0C;
1686 break;
1687 case 0x44: // 'D'
1688 case 0x64: // 'd'
1689 byteValue |= (byte) 0x0D;
1690 break;
1691 case 0x45: // 'E'
1692 case 0x65: // 'e'
1693 byteValue |= (byte) 0x0E;
1694 break;
1695 case 0x46: // 'F'
1696 case 0x66: // 'f'
1697 byteValue |= (byte) 0x0F;
1698 break;
1699 default:
1700 Message message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get(
1701 filterString, equalPos+i+1);
1702 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1703 }
1704
1705 valueBuffer.put(byteValue);
1706 }
1707 else
1708 {
1709 valueBuffer.put(valueBytes[i]);
1710 }
1711 }
1712
1713 valueBytes = new byte[valueBuffer.position()];
1714 valueBuffer.flip();
1715 valueBuffer.get(valueBytes);
1716 value = new ASN1OctetString(valueBytes);
1717 }
1718 else
1719 {
1720 value = new ASN1OctetString(valueBytes);
1721 }
1722
1723
1724 // Make sure that the filter has at least one of an attribute description
1725 // and/or a matching rule ID.
1726 if ((attributeType == null) && (matchingRuleID == null))
1727 {
1728 Message message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR.get(
1729 filterString, startPos);
1730 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
1731 }
1732
1733
1734 return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
1735 attributeType, value, null, null, null,
1736 matchingRuleID, dnAttributes);
1737 }
1738
1739
1740
1741 /**
1742 * Retrieves the filter type for this search filter.
1743 *
1744 * @return The filter type for this search filter.
1745 */
1746 public FilterType getFilterType()
1747 {
1748 return filterType;
1749 }
1750
1751
1752
1753 /**
1754 * Retrieves the set of subordinate filter components for AND or OR searches.
1755 * The contents of the returned list may be altered by the caller.
1756 *
1757 * @return The set of subordinate filter components for AND and OR searches,
1758 * or <CODE>null</CODE> if this is not an AND or OR search.
1759 */
1760 public ArrayList<RawFilter> getFilterComponents()
1761 {
1762 return filterComponents;
1763 }
1764
1765
1766
1767 /**
1768 * Specifies the set of subordinate filter components for AND or OR searches.
1769 * This will be ignored for all other filter types.
1770 *
1771 * @param filterComponents The set of subordinate filter components for AND
1772 * or OR searches.
1773 */
1774 public void setFilterComponents(ArrayList<RawFilter> filterComponents)
1775 {
1776 this.filterComponents = filterComponents;
1777 }
1778
1779
1780
1781 /**
1782 * Retrieves the subordinate filter component for NOT searches.
1783 *
1784 * @return The subordinate filter component for NOT searches, or
1785 * <CODE>null</CODE> if this is not a NOT search.
1786 */
1787 public RawFilter getNOTComponent()
1788 {
1789 return notComponent;
1790 }
1791
1792
1793
1794 /**
1795 * Specifies the subordinate filter component for NOT searches. This will be
1796 * ignored for any other type of search.
1797 *
1798 * @param notComponent The subordinate filter component for NOT searches.
1799 */
1800 public void setNOTComponent(RawFilter notComponent)
1801 {
1802 this.notComponent = notComponent;
1803 }
1804
1805
1806
1807 /**
1808 * Retrieves the attribute type for this search filter. This will not be
1809 * applicable for AND, OR, or NOT filters.
1810 *
1811 * @return The attribute type for this search filter, or <CODE>null</CODE> if
1812 * there is none.
1813 */
1814 public String getAttributeType()
1815 {
1816 return attributeType;
1817 }
1818
1819
1820
1821 /**
1822 * Specifies the attribute type for this search filter. This will be ignored
1823 * for AND, OR, and NOT searches.
1824 *
1825 * @param attributeType The attribute type for this search filter.
1826 */
1827 public void setAttributeType(String attributeType)
1828 {
1829 this.attributeType = attributeType;
1830 }
1831
1832
1833
1834 /**
1835 * Retrieves the assertion value for this search filter. This will only be
1836 * applicable for equality, greater or equal, less or equal, approximate, or
1837 * extensible matching filters.
1838 *
1839 * @return The assertion value for this search filter, or <CODE>null</CODE>
1840 * if there is none.
1841 */
1842 public ByteString getAssertionValue()
1843 {
1844 return assertionValue;
1845 }
1846
1847
1848
1849 /**
1850 * Specifies the assertion value for this search filter. This will be ignored
1851 * for types of filters that do not have an assertion value.
1852 *
1853 * @param assertionValue The assertion value for this search filter.
1854 */
1855 public void setAssertionValue(ByteString assertionValue)
1856 {
1857 this.assertionValue = assertionValue;
1858 }
1859
1860
1861
1862 /**
1863 * Retrieves the subInitial component for this substring filter. This is only
1864 * applicable for substring search filters, but even substring filters might
1865 * not have a value for this component.
1866 *
1867 * @return The subInitial component for this substring filter, or
1868 * <CODE>null</CODE> if there is none.
1869 */
1870 public ByteString getSubInitialElement()
1871 {
1872 return subInitialElement;
1873 }
1874
1875
1876
1877 /**
1878 * Specifies the subInitial element for this substring filter. This will be
1879 * ignored for all other types of filters.
1880 *
1881 * @param subInitialElement The subInitial element for this substring
1882 * filter.
1883 */
1884 public void setSubInitialElement(ByteString subInitialElement)
1885 {
1886 this.subInitialElement = subInitialElement;
1887 }
1888
1889
1890
1891 /**
1892 * Retrieves the set of subAny elements for this substring filter. This is
1893 * only applicable for substring search filters, and even then may be null or
1894 * empty for some substring filters.
1895 *
1896 * @return The set of subAny elements for this substring filter, or
1897 * <CODE>null</CODE> if there are none.
1898 */
1899 public ArrayList<ByteString> getSubAnyElements()
1900 {
1901 return subAnyElements;
1902 }
1903
1904
1905
1906 /**
1907 * Specifies the set of subAny values for this substring filter. This will be
1908 * ignored for other filter types.
1909 *
1910 * @param subAnyElements The set of subAny elements for this substring
1911 * filter.
1912 */
1913 public void setSubAnyElements(ArrayList<ByteString> subAnyElements)
1914 {
1915 this.subAnyElements = subAnyElements;
1916 }
1917
1918
1919
1920 /**
1921 * Retrieves the subFinal element for this substring filter. This is not
1922 * applicable for any other filter type, and may not be provided even for some
1923 * substring filters.
1924 *
1925 * @return The subFinal element for this substring filter, or
1926 * <CODE>null</CODE> if there is none.
1927 */
1928 public ByteString getSubFinalElement()
1929 {
1930 return subFinalElement;
1931 }
1932
1933
1934
1935 /**
1936 * Specifies the subFinal element for this substring filter. This will be
1937 * ignored for all other filter types.
1938 *
1939 * @param subFinalElement The subFinal element for this substring filter.
1940 */
1941 public void setSubFinalElement(ByteString subFinalElement)
1942 {
1943 this.subFinalElement = subFinalElement;
1944 }
1945
1946
1947
1948 /**
1949 * Retrieves the matching rule ID for this extensible match filter. This is
1950 * not applicable for any other type of filter and may not be included in
1951 * some extensible matching filters.
1952 *
1953 * @return The matching rule ID for this extensible match filter, or
1954 * <CODE>null</CODE> if there is none.
1955 */
1956 public String getMatchingRuleID()
1957 {
1958 return matchingRuleID;
1959 }
1960
1961
1962
1963 /**
1964 * Specifies the matching rule ID for this extensible match filter. It will
1965 * be ignored for all other filter types.
1966 *
1967 * @param matchingRuleID The matching rule ID for this extensible match
1968 * filter.
1969 */
1970 public void setMatchingRuleID(String matchingRuleID)
1971 {
1972 this.matchingRuleID = matchingRuleID;
1973 }
1974
1975
1976
1977 /**
1978 * Retrieves the value of the DN attributes flag for this extensible match
1979 * filter, which indicates whether to perform matching on the components of
1980 * the DN. This does not apply for any other type of filter.
1981 *
1982 * @return The value of the DN attributes flag for this extensibleMatch
1983 * filter.
1984 */
1985 public boolean getDNAttributes()
1986 {
1987 return dnAttributes;
1988 }
1989
1990
1991
1992 /**
1993 * Specifies the value of the DN attributes flag for this extensible match
1994 * filter. It will be ignored for all other filter types.
1995 *
1996 * @param dnAttributes The value of the DN attributes flag for this
1997 * extensible match filter.
1998 */
1999 public void setDNAttributes(boolean dnAttributes)
2000 {
2001 this.dnAttributes = dnAttributes;
2002 }
2003
2004
2005
2006 /**
2007 * Converts this LDAP filter to a search filter that may be used by the
2008 * Directory Server's core processing.
2009 *
2010 * @return The generated search filter.
2011 *
2012 * @throws DirectoryException If a problem occurs while attempting to
2013 * construct the search filter.
2014 */
2015 public SearchFilter toSearchFilter()
2016 throws DirectoryException
2017 {
2018 ArrayList<SearchFilter> subComps;
2019 if (filterComponents == null)
2020 {
2021 subComps = null;
2022 }
2023 else
2024 {
2025 subComps = new ArrayList<SearchFilter>(filterComponents.size());
2026 for (RawFilter f : filterComponents)
2027 {
2028 subComps.add(f.toSearchFilter());
2029 }
2030 }
2031
2032
2033 SearchFilter notComp;
2034 if (notComponent == null)
2035 {
2036 notComp = null;
2037 }
2038 else
2039 {
2040 notComp = notComponent.toSearchFilter();
2041 }
2042
2043
2044 AttributeType attrType;
2045 HashSet<String> options;
2046 if (attributeType == null)
2047 {
2048 attrType = null;
2049 options = null;
2050 }
2051 else
2052 {
2053 int semicolonPos = attributeType.indexOf(';');
2054 if (semicolonPos > 0)
2055 {
2056 String baseName = attributeType.substring(0, semicolonPos);
2057 attrType = DirectoryServer.getAttributeType(toLowerCase(baseName));
2058 if (attrType == null)
2059 {
2060 attrType = DirectoryServer.getDefaultAttributeType(baseName);
2061 }
2062
2063 options = new HashSet<String>();
2064 StringTokenizer tokenizer =
2065 new StringTokenizer(attributeType.substring(semicolonPos+1), ";");
2066 while (tokenizer.hasMoreTokens())
2067 {
2068 options.add(tokenizer.nextToken());
2069 }
2070 }
2071 else
2072 {
2073 options = null;
2074 attrType =
2075 DirectoryServer.getAttributeType(toLowerCase(attributeType));
2076 if (attrType == null)
2077 {
2078 attrType = DirectoryServer.getDefaultAttributeType(attributeType);
2079 }
2080 }
2081 }
2082
2083
2084 AttributeValue value;
2085 if (assertionValue == null)
2086 {
2087 value = null;
2088 }
2089 else if (attrType == null)
2090 {
2091 if (matchingRuleID == null)
2092 {
2093 Message message = ERR_LDAP_FILTER_VALUE_WITH_NO_ATTR_OR_MR.get();
2094 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
2095 }
2096 else
2097 {
2098 MatchingRule mr =
2099 DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID));
2100 if (mr == null)
2101 {
2102 Message message =
2103 ERR_LDAP_FILTER_UNKNOWN_MATCHING_RULE.get(matchingRuleID);
2104 throw new DirectoryException(ResultCode.INAPPROPRIATE_MATCHING,
2105 message);
2106 }
2107 else
2108 {
2109 ByteString normalizedValue = mr.normalizeValue(assertionValue);
2110 value = new AttributeValue(assertionValue, normalizedValue);
2111 }
2112 }
2113 }
2114 else
2115 {
2116 value = new AttributeValue(attrType, assertionValue);
2117 }
2118
2119
2120 ArrayList<ByteString> subAnyComps;
2121 if (subAnyElements == null)
2122 {
2123 subAnyComps = null;
2124 }
2125 else
2126 {
2127 subAnyComps = new ArrayList<ByteString>(subAnyElements);
2128 }
2129
2130
2131 return new SearchFilter(filterType, subComps, notComp, attrType,
2132 options, value, subInitialElement, subAnyComps,
2133 subFinalElement, matchingRuleID, dnAttributes);
2134 }
2135
2136
2137
2138 /**
2139 * Appends a string representation of this search filter to the provided
2140 * buffer.
2141 *
2142 * @param buffer The buffer to which the information should be appended.
2143 */
2144 public void toString(StringBuilder buffer)
2145 {
2146 switch (filterType)
2147 {
2148 case AND:
2149 buffer.append("(&");
2150 for (RawFilter f : filterComponents)
2151 {
2152 f.toString(buffer);
2153 }
2154 buffer.append(")");
2155 break;
2156 case OR:
2157 buffer.append("(|");
2158 for (RawFilter f : filterComponents)
2159 {
2160 f.toString(buffer);
2161 }
2162 buffer.append(")");
2163 break;
2164 case NOT:
2165 buffer.append("(!");
2166 notComponent.toString(buffer);
2167 buffer.append(")");
2168 break;
2169 case EQUALITY:
2170 buffer.append("(");
2171 buffer.append(attributeType);
2172 buffer.append("=");
2173 valueToFilterString(buffer, assertionValue);
2174 buffer.append(")");
2175 break;
2176 case SUBSTRING:
2177 buffer.append("(");
2178 buffer.append(attributeType);
2179 buffer.append("=");
2180
2181 if (subInitialElement != null)
2182 {
2183 valueToFilterString(buffer, subInitialElement);
2184 }
2185
2186 if ((subAnyElements != null) && (! subAnyElements.isEmpty()))
2187 {
2188 for (ByteString s : subAnyElements)
2189 {
2190 buffer.append("*");
2191 valueToFilterString(buffer, s);
2192 }
2193 }
2194
2195 buffer.append("*");
2196
2197 if (subFinalElement != null)
2198 {
2199 valueToFilterString(buffer, subFinalElement);
2200 }
2201
2202 buffer.append(")");
2203 break;
2204 case GREATER_OR_EQUAL:
2205 buffer.append("(");
2206 buffer.append(attributeType);
2207 buffer.append(">=");
2208 valueToFilterString(buffer, assertionValue);
2209 buffer.append(")");
2210 break;
2211 case LESS_OR_EQUAL:
2212 buffer.append("(");
2213 buffer.append(attributeType);
2214 buffer.append("<=");
2215 valueToFilterString(buffer, assertionValue);
2216 buffer.append(")");
2217 break;
2218 case PRESENT:
2219 buffer.append("(");
2220 buffer.append(attributeType);
2221 buffer.append("=*)");
2222 break;
2223 case APPROXIMATE_MATCH:
2224 buffer.append("(");
2225 buffer.append(attributeType);
2226 buffer.append("~=");
2227 valueToFilterString(buffer, assertionValue);
2228 buffer.append(")");
2229 break;
2230 case EXTENSIBLE_MATCH:
2231 buffer.append("(");
2232
2233 if (attributeType != null)
2234 {
2235 buffer.append(attributeType);
2236 }
2237
2238 if (dnAttributes)
2239 {
2240 buffer.append(":dn");
2241 }
2242
2243 if (matchingRuleID != null)
2244 {
2245 buffer.append(":");
2246 buffer.append(matchingRuleID);
2247 }
2248
2249 buffer.append(":=");
2250 valueToFilterString(buffer, assertionValue);
2251 buffer.append(")");
2252 break;
2253 }
2254 }
2255 }
2256