001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 * Portions Copyright 2013-2014 Manuel Gaupp 017 */ 018package org.opends.server.types; 019 020import static org.opends.messages.CoreMessages.*; 021import static org.opends.server.util.ServerConstants.*; 022import static org.opends.server.util.StaticUtils.*; 023 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.LinkedHashSet; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Set; 032 033import org.forgerock.i18n.LocalizableMessage; 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035import org.forgerock.opendj.ldap.AVA; 036import org.forgerock.opendj.ldap.Assertion; 037import org.forgerock.opendj.ldap.AttributeDescription; 038import org.forgerock.opendj.ldap.ByteString; 039import org.forgerock.opendj.ldap.ByteStringBuilder; 040import org.forgerock.opendj.ldap.ConditionResult; 041import org.forgerock.opendj.ldap.RDN; 042import org.forgerock.opendj.ldap.ResultCode; 043import org.forgerock.opendj.ldap.schema.AttributeType; 044import org.forgerock.opendj.ldap.schema.MatchingRule; 045import org.opends.server.core.DirectoryServer; 046 047/** 048 * This class defines a data structure for storing and interacting 049 * with a search filter that may serve as criteria for locating 050 * entries in the Directory Server. 051 */ 052@org.opends.server.types.PublicAPI( 053 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 054 mayInstantiate=true, 055 mayExtend=false, 056 mayInvoke=true) 057public final class SearchFilter 058{ 059 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 060 061 private static SearchFilter objectClassPresent; 062 063 /** The attribute description for this filter. */ 064 private final AttributeDescription attributeDescription; 065 /** The attribute type for this filter. */ 066 private final AttributeType attributeType; 067 068 /** The assertion value for this filter. */ 069 private final ByteString assertionValue; 070 071 /** Indicates whether to match on DN attributes for extensible match filters. */ 072 private final boolean dnAttributes; 073 074 /** The subInitial element for substring filters. */ 075 private final ByteString subInitialElement; 076 /** The set of subAny components for substring filters. */ 077 private final List<ByteString> subAnyElements; 078 /** The subFinal element for substring filters. */ 079 private final ByteString subFinalElement; 080 081 /** The search filter type for this filter. */ 082 private final FilterType filterType; 083 084 /** The set of filter components for AND and OR filters. */ 085 private final LinkedHashSet<SearchFilter> filterComponents; 086 /** The not filter component for this search filter. */ 087 private final SearchFilter notComponent; 088 089 /** The matching rule ID for this search filter. */ 090 private final String matchingRuleID; 091 092 093 094 /** 095 * Creates a new search filter with the provided information. 096 * 097 * @param filterType The filter type for this search 098 * filter. 099 * @param filterComponents The set of filter components for AND 100 * and OR filters. 101 * @param notComponent The filter component for NOT filters. 102 * @param attributeType The attribute type for this filter. 103 * @param attributeOptions The set of attribute options for the 104 * associated attribute type. 105 * @param assertionValue The assertion value for this filter. 106 * @param subInitialElement The subInitial element for substring 107 * filters. 108 * @param subAnyElements The subAny elements for substring 109 * filters. 110 * @param subFinalElement The subFinal element for substring 111 * filters. 112 * @param matchingRuleID The matching rule ID for this search 113 * filter. 114 * @param dnAttributes Indicates whether to match on DN 115 * attributes for extensible match 116 * filters. 117 * 118 * FIXME: this should be private. 119 */ 120 public SearchFilter(FilterType filterType, 121 Collection<SearchFilter> filterComponents, 122 SearchFilter notComponent, 123 AttributeType attributeType, 124 Set<String> attributeOptions, 125 ByteString assertionValue, 126 ByteString subInitialElement, 127 List<ByteString> subAnyElements, 128 ByteString subFinalElement, 129 String matchingRuleID, boolean dnAttributes) 130 { 131 // This used to happen in getSubAnyElements, but we do it here 132 // so that we can make this.subAnyElements final. 133 if (subAnyElements == null) { 134 subAnyElements = new ArrayList<>(0); 135 } 136 137 // This used to happen in getFilterComponents, but we do it here 138 // so that we can make this.filterComponents final. 139 if (filterComponents == null) { 140 filterComponents = Collections.emptyList(); 141 } 142 143 this.filterType = filterType; 144 this.filterComponents = new LinkedHashSet<>(filterComponents); 145 this.notComponent = notComponent; 146 this.attributeDescription = attributeType != null 147 ? AttributeDescription.create(attributeType, attributeOptions) 148 : null; 149 this.attributeType = attributeType; 150 this.assertionValue = assertionValue; 151 this.subInitialElement = subInitialElement; 152 this.subAnyElements = subAnyElements; 153 this.subFinalElement = subFinalElement; 154 this.matchingRuleID = matchingRuleID; 155 this.dnAttributes = dnAttributes; 156 } 157 158 159 /** 160 * Creates a new AND search filter with the provided information. 161 * 162 * @param filterComponents The set of filter components for the 163 * AND filter. 164 * 165 * @return The constructed search filter. 166 */ 167 public static SearchFilter createANDFilter(Collection<SearchFilter> 168 filterComponents) 169 { 170 return new SearchFilter(FilterType.AND, filterComponents, null, 171 null, null, null, null, null, null, null, 172 false); 173 } 174 175 176 177 /** 178 * Creates a new OR search filter with the provided information. 179 * 180 * @param filterComponents The set of filter components for the OR 181 * filter. 182 * 183 * @return The constructed search filter. 184 */ 185 public static SearchFilter createORFilter(Collection<SearchFilter> 186 filterComponents) 187 { 188 return new SearchFilter(FilterType.OR, filterComponents, null, 189 null, null, null, null, null, null, null, 190 false); 191 } 192 193 194 195 /** 196 * Creates a new NOT search filter with the provided information. 197 * 198 * @param notComponent The filter component for this NOT filter. 199 * 200 * @return The constructed search filter. 201 */ 202 public static SearchFilter createNOTFilter( 203 SearchFilter notComponent) 204 { 205 return new SearchFilter(FilterType.NOT, null, notComponent, null, 206 null, null, null, null, null, null, 207 false); 208 } 209 210 211 212 /** 213 * Creates a new equality search filter with the provided 214 * information. 215 * 216 * @param attributeType The attribute type for this equality 217 * filter. 218 * @param assertionValue The assertion value for this equality 219 * filter. 220 * 221 * @return The constructed search filter. 222 */ 223 public static SearchFilter createEqualityFilter( 224 AttributeType attributeType, 225 ByteString assertionValue) 226 { 227 return new SearchFilter(FilterType.EQUALITY, null, null, 228 attributeType, null, assertionValue, null, 229 null, null, null, false); 230 } 231 232 233 234 /** 235 * Creates a new equality search filter with the provided 236 * information. 237 * 238 * @param attributeType The attribute type for this equality 239 * filter. 240 * @param attributeOptions The set of attribute options for this 241 * equality filter. 242 * @param assertionValue The assertion value for this equality 243 * filter. 244 * 245 * @return The constructed search filter. 246 */ 247 public static SearchFilter createEqualityFilter( 248 AttributeType attributeType, 249 Set<String> attributeOptions, 250 ByteString assertionValue) 251 { 252 return new SearchFilter(FilterType.EQUALITY, null, null, 253 attributeType, attributeOptions, 254 assertionValue, null, null, null, null, 255 false); 256 } 257 258 259 260 /** 261 * Creates a new substring search filter with the provided 262 * information. 263 * 264 * @param attributeType The attribute type for this filter. 265 * @param subInitialElement The subInitial element for substring 266 * filters. 267 * @param subAnyElements The subAny elements for substring 268 * filters. 269 * @param subFinalElement The subFinal element for substring 270 * filters. 271 * 272 * @return The constructed search filter. 273 */ 274 public static SearchFilter 275 createSubstringFilter(AttributeType attributeType, 276 ByteString subInitialElement, 277 List<ByteString> subAnyElements, 278 ByteString subFinalElement) 279 { 280 return new SearchFilter(FilterType.SUBSTRING, null, null, 281 attributeType, null, null, 282 subInitialElement, subAnyElements, 283 subFinalElement, null, false); 284 } 285 286 287 288 /** 289 * Creates a new substring search filter with the provided 290 * information. 291 * 292 * @param attributeType The attribute type for this filter. 293 * @param attributeOptions The set of attribute options for this 294 * search filter. 295 * @param subInitialElement The subInitial element for substring 296 * filters. 297 * @param subAnyElements The subAny elements for substring 298 * filters. 299 * @param subFinalElement The subFinal element for substring 300 * filters. 301 * 302 * @return The constructed search filter. 303 */ 304 public static SearchFilter 305 createSubstringFilter(AttributeType attributeType, 306 Set<String> attributeOptions, 307 ByteString subInitialElement, 308 List<ByteString> subAnyElements, 309 ByteString subFinalElement) 310 { 311 return new SearchFilter(FilterType.SUBSTRING, null, null, 312 attributeType, attributeOptions, null, 313 subInitialElement, subAnyElements, 314 subFinalElement, null, false); 315 } 316 317 318 319 /** 320 * Creates a greater-or-equal search filter with the provided 321 * information. 322 * 323 * @param attributeType The attribute type for this 324 * greater-or-equal filter. 325 * @param assertionValue The assertion value for this 326 * greater-or-equal filter. 327 * 328 * @return The constructed search filter. 329 */ 330 public static SearchFilter createGreaterOrEqualFilter( 331 AttributeType attributeType, 332 ByteString assertionValue) 333 { 334 return new SearchFilter(FilterType.GREATER_OR_EQUAL, null, null, 335 attributeType, null, assertionValue, null, 336 null, null, null, false); 337 } 338 339 340 341 /** 342 * Creates a greater-or-equal search filter with the provided 343 * information. 344 * 345 * @param attributeType The attribute type for this 346 * greater-or-equal filter. 347 * @param attributeOptions The set of attribute options for this 348 * search filter. 349 * @param assertionValue The assertion value for this 350 * greater-or-equal filter. 351 * 352 * @return The constructed search filter. 353 */ 354 public static SearchFilter createGreaterOrEqualFilter( 355 AttributeType attributeType, 356 Set<String> attributeOptions, 357 ByteString assertionValue) 358 { 359 return new SearchFilter(FilterType.GREATER_OR_EQUAL, null, null, 360 attributeType, attributeOptions, 361 assertionValue, null, null, null, null, 362 false); 363 } 364 365 366 367 /** 368 * Creates a less-or-equal search filter with the provided 369 * information. 370 * 371 * @param attributeType The attribute type for this less-or-equal 372 * filter. 373 * @param assertionValue The assertion value for this 374 * less-or-equal filter. 375 * 376 * @return The constructed search filter. 377 */ 378 public static SearchFilter createLessOrEqualFilter( 379 AttributeType attributeType, 380 ByteString assertionValue) 381 { 382 return new SearchFilter(FilterType.LESS_OR_EQUAL, null, null, 383 attributeType, null, assertionValue, null, 384 null, null, null, false); 385 } 386 387 388 389 /** 390 * Creates a less-or-equal search filter with the provided 391 * information. 392 * 393 * @param attributeType The attribute type for this 394 * less-or-equal filter. 395 * @param attributeOptions The set of attribute options for this 396 * search filter. 397 * @param assertionValue The assertion value for this 398 * less-or-equal filter. 399 * 400 * @return The constructed search filter. 401 */ 402 public static SearchFilter createLessOrEqualFilter( 403 AttributeType attributeType, 404 Set<String> attributeOptions, 405 ByteString assertionValue) 406 { 407 return new SearchFilter(FilterType.LESS_OR_EQUAL, null, null, 408 attributeType, attributeOptions, 409 assertionValue, null, null, null, null, 410 false); 411 } 412 413 414 415 /** 416 * Creates a presence search filter with the provided information. 417 * 418 * @param attributeType The attribute type for this presence 419 * filter. 420 * 421 * @return The constructed search filter. 422 */ 423 public static SearchFilter createPresenceFilter( 424 AttributeType attributeType) 425 { 426 return new SearchFilter(FilterType.PRESENT, null, null, 427 attributeType, null, null, null, null, 428 null, null, false); 429 } 430 431 432 433 /** 434 * Creates a presence search filter with the provided information. 435 * 436 * @param attributeType The attribute type for this presence 437 * filter. 438 * @param attributeOptions The attribute options for this presence 439 * filter. 440 * 441 * @return The constructed search filter. 442 */ 443 public static SearchFilter createPresenceFilter( 444 AttributeType attributeType, 445 Set<String> attributeOptions) 446 { 447 return new SearchFilter(FilterType.PRESENT, null, null, 448 attributeType, attributeOptions, null, 449 null, null, null, null, false); 450 } 451 452 453 454 /** 455 * Creates an approximate search filter with the provided 456 * information. 457 * 458 * @param attributeType The attribute type for this approximate 459 * filter. 460 * @param assertionValue The assertion value for this approximate 461 * filter. 462 * 463 * @return The constructed search filter. 464 */ 465 public static SearchFilter createApproximateFilter( 466 AttributeType attributeType, 467 ByteString assertionValue) 468 { 469 return new SearchFilter(FilterType.APPROXIMATE_MATCH, null, null, 470 attributeType, null, assertionValue, null, 471 null, null, null, false); 472 } 473 474 475 476 /** 477 * Creates an approximate search filter with the provided 478 * information. 479 * 480 * @param attributeType The attribute type for this approximate 481 * filter. 482 * @param attributeOptions The attribute options for this 483 * approximate filter. 484 * @param assertionValue The assertion value for this 485 * approximate filter. 486 * 487 * @return The constructed search filter. 488 */ 489 public static SearchFilter createApproximateFilter( 490 AttributeType attributeType, 491 Set<String> attributeOptions, 492 ByteString assertionValue) 493 { 494 return new SearchFilter(FilterType.APPROXIMATE_MATCH, null, null, 495 attributeType, attributeOptions, 496 assertionValue, null, null, null, null, 497 false); 498 } 499 500 501 502 /** 503 * Creates an extensible matching filter with the provided 504 * information. 505 * 506 * @param attributeType The attribute type for this extensible 507 * match filter. 508 * @param assertionValue The assertion value for this extensible 509 * match filter. 510 * @param matchingRuleID The matching rule ID for this search 511 * filter. 512 * @param dnAttributes Indicates whether to match on DN 513 * attributes for extensible match filters. 514 * 515 * @return The constructed search filter. 516 * 517 * @throws DirectoryException If the provided information is not 518 * sufficient to create an extensible 519 * match filter. 520 */ 521 public static SearchFilter createExtensibleMatchFilter( 522 AttributeType attributeType, 523 ByteString assertionValue, 524 String matchingRuleID, 525 boolean dnAttributes) 526 throws DirectoryException 527 { 528 if (attributeType == null && matchingRuleID == null) 529 { 530 LocalizableMessage message = 531 ERR_SEARCH_FILTER_CREATE_EXTENSIBLE_MATCH_NO_AT_OR_MR.get(); 532 throw new DirectoryException( 533 ResultCode.PROTOCOL_ERROR, message); 534 } 535 536 return new SearchFilter(FilterType.EXTENSIBLE_MATCH, null, null, 537 attributeType, null, assertionValue, null, 538 null, null, matchingRuleID, dnAttributes); 539 } 540 541 542 543 /** 544 * Creates an extensible matching filter with the provided 545 * information. 546 * 547 * @param attributeType The attribute type for this extensible 548 * match filter. 549 * @param attributeOptions The set of attribute options for this 550 * extensible match filter. 551 * @param assertionValue The assertion value for this extensible 552 * match filter. 553 * @param matchingRuleID The matching rule ID for this search 554 * filter. 555 * @param dnAttributes Indicates whether to match on DN 556 * attributes for extensible match 557 * filters. 558 * 559 * @return The constructed search filter. 560 * 561 * @throws DirectoryException If the provided information is not 562 * sufficient to create an extensible 563 * match filter. 564 */ 565 public static SearchFilter createExtensibleMatchFilter( 566 AttributeType attributeType, 567 Set<String> attributeOptions, 568 ByteString assertionValue, 569 String matchingRuleID, 570 boolean dnAttributes) 571 throws DirectoryException 572 { 573 if (attributeType == null && matchingRuleID == null) 574 { 575 LocalizableMessage message = 576 ERR_SEARCH_FILTER_CREATE_EXTENSIBLE_MATCH_NO_AT_OR_MR.get(); 577 throw new DirectoryException( 578 ResultCode.PROTOCOL_ERROR, message); 579 } 580 581 return new SearchFilter(FilterType.EXTENSIBLE_MATCH, null, null, 582 attributeType, attributeOptions, 583 assertionValue, null, null, null, 584 matchingRuleID, dnAttributes); 585 } 586 587 588 589 /** 590 * Decodes the provided filter string as a search filter. 591 * 592 * @param filterString The filter string to be decoded as a search 593 * filter. 594 * 595 * @return The search filter decoded from the provided string. 596 * 597 * @throws DirectoryException If a problem occurs while attempting 598 * to decode the provided string as a 599 * search filter. 600 */ 601 public static SearchFilter createFilterFromString( 602 String filterString) 603 throws DirectoryException 604 { 605 if (filterString == null) 606 { 607 LocalizableMessage message = ERR_SEARCH_FILTER_NULL.get(); 608 throw new DirectoryException( 609 ResultCode.PROTOCOL_ERROR, message); 610 } 611 612 613 try 614 { 615 return createFilterFromString(filterString, 0, 616 filterString.length()); 617 } 618 catch (DirectoryException de) 619 { 620 logger.traceException(de); 621 622 throw de; 623 } 624 catch (Exception e) 625 { 626 logger.traceException(e); 627 628 LocalizableMessage message = ERR_SEARCH_FILTER_UNCAUGHT_EXCEPTION.get(filterString, e); 629 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e); 630 } 631 } 632 633 634 635 /** 636 * Creates a new search filter from the specified portion of the 637 * provided string. 638 * 639 * @param filterString The string containing the filter 640 * information to be decoded. 641 * @param startPos The index of the first character in the 642 * string that is part of the search filter. 643 * @param endPos The index of the first character after the 644 * start position that is not part of the 645 * search filter. 646 * 647 * @return The decoded search filter. 648 * 649 * @throws DirectoryException If a problem occurs while attempting 650 * to decode the provided string as a 651 * search filter. 652 */ 653 private static SearchFilter createFilterFromString( 654 String filterString, int startPos, 655 int endPos) 656 throws DirectoryException 657 { 658 // Make sure that the length is sufficient for a valid search 659 // filter. 660 int length = endPos - startPos; 661 if (length <= 0) 662 { 663 LocalizableMessage message = ERR_SEARCH_FILTER_NULL.get(); 664 throw new DirectoryException( 665 ResultCode.PROTOCOL_ERROR, message); 666 } 667 668 669 // If the filter is surrounded by parentheses (which it should 670 // be), then strip them off. 671 if (filterString.charAt(startPos) == '(') 672 { 673 if (filterString.charAt(endPos-1) == ')') 674 { 675 startPos++; 676 endPos--; 677 } 678 else 679 { 680 LocalizableMessage message = ERR_SEARCH_FILTER_MISMATCHED_PARENTHESES. 681 get(filterString, startPos, endPos); 682 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 683 message); 684 } 685 } 686 687 688 // Look at the first character. If it is a '&' then it is an AND 689 // search. If it is a '|' then it is an OR search. If it is a 690 // '!' then it is a NOT search. 691 char c = filterString.charAt(startPos); 692 if (c == '&') 693 { 694 return decodeCompoundFilter(FilterType.AND, filterString, 695 startPos+1, endPos); 696 } 697 else if (c == '|') 698 { 699 return decodeCompoundFilter(FilterType.OR, filterString, 700 startPos+1, endPos); 701 } 702 else if (c == '!') 703 { 704 return decodeCompoundFilter(FilterType.NOT, filterString, 705 startPos+1, endPos); 706 } 707 708 709 // If we've gotten here, then it must be a simple filter. It must 710 // have an equal sign at some point, so find it. 711 int equalPos = -1; 712 for (int i=startPos; i < endPos; i++) 713 { 714 if (filterString.charAt(i) == '=') 715 { 716 equalPos = i; 717 break; 718 } 719 } 720 721 if (equalPos <= startPos) 722 { 723 LocalizableMessage message = ERR_SEARCH_FILTER_NO_EQUAL_SIGN.get( 724 filterString, startPos, endPos); 725 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 726 message); 727 } 728 729 730 // Look at the character immediately before the equal sign, 731 // because it may help determine the filter type. 732 int attrEndPos; 733 FilterType filterType; 734 switch (filterString.charAt(equalPos-1)) 735 { 736 case '~': 737 filterType = FilterType.APPROXIMATE_MATCH; 738 attrEndPos = equalPos-1; 739 break; 740 case '>': 741 filterType = FilterType.GREATER_OR_EQUAL; 742 attrEndPos = equalPos-1; 743 break; 744 case '<': 745 filterType = FilterType.LESS_OR_EQUAL; 746 attrEndPos = equalPos-1; 747 break; 748 case ':': 749 return decodeExtensibleMatchFilter(filterString, startPos, 750 equalPos, endPos); 751 default: 752 filterType = FilterType.EQUALITY; 753 attrEndPos = equalPos; 754 break; 755 } 756 757 758 // The part of the filter string before the equal sign should be 759 // the attribute type (with or without options). Decode it. 760 String attrType = filterString.substring(startPos, attrEndPos); 761 StringBuilder lowerType = new StringBuilder(attrType.length()); 762 Set<String> attributeOptions = new HashSet<>(); 763 764 int semicolonPos = attrType.indexOf(';'); 765 if (semicolonPos < 0) 766 { 767 for (int i=0; i < attrType.length(); i++) 768 { 769 lowerType.append(Character.toLowerCase(attrType.charAt(i))); 770 } 771 } 772 else 773 { 774 for (int i=0; i < semicolonPos; i++) 775 { 776 lowerType.append(Character.toLowerCase(attrType.charAt(i))); 777 } 778 779 int nextPos = attrType.indexOf(';', semicolonPos+1); 780 while (nextPos > 0) 781 { 782 attributeOptions.add(attrType.substring(semicolonPos+1, 783 nextPos)); 784 semicolonPos = nextPos; 785 nextPos = attrType.indexOf(';', semicolonPos+1); 786 } 787 788 attributeOptions.add(attrType.substring(semicolonPos+1)); 789 } 790 791 // Get the attribute value. 792 AttributeType attributeType = getAttributeType(attrType, lowerType); 793 String valueStr = filterString.substring(equalPos+1, endPos); 794 if (valueStr.length() == 0) 795 { 796 return new SearchFilter(filterType, null, null, attributeType, 797 attributeOptions, ByteString.empty(), 798 null, null, null, null, false); 799 } 800 else if (valueStr.equals("*")) 801 { 802 return new SearchFilter(FilterType.PRESENT, null, null, 803 attributeType, attributeOptions, null, 804 null, null, null, null, false); 805 } 806 else if (valueStr.indexOf('*') >= 0) 807 { 808 return decodeSubstringFilter(filterString, attributeType, 809 attributeOptions, equalPos, 810 endPos); 811 } 812 else 813 { 814 boolean hasEscape = false; 815 byte[] valueBytes = getBytes(valueStr); 816 for (byte valueByte : valueBytes) 817 { 818 if (valueByte == 0x5C) // The backslash character 819 { 820 hasEscape = true; 821 break; 822 } 823 } 824 825 ByteString userValue; 826 if (hasEscape) 827 { 828 ByteStringBuilder valueBuffer = 829 new ByteStringBuilder(valueStr.length()); 830 for (int i=0; i < valueBytes.length; i++) 831 { 832 if (valueBytes[i] == 0x5C) // The backslash character 833 { 834 // The next two bytes must be the hex characters that 835 // comprise the binary value. 836 if (i + 2 >= valueBytes.length) 837 { 838 LocalizableMessage message = 839 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 840 get(filterString, equalPos+i+1); 841 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 842 message); 843 } 844 845 byte byteValue = 0; 846 switch (valueBytes[++i]) 847 { 848 case 0x30: // '0' 849 break; 850 case 0x31: // '1' 851 byteValue = (byte) 0x10; 852 break; 853 case 0x32: // '2' 854 byteValue = (byte) 0x20; 855 break; 856 case 0x33: // '3' 857 byteValue = (byte) 0x30; 858 break; 859 case 0x34: // '4' 860 byteValue = (byte) 0x40; 861 break; 862 case 0x35: // '5' 863 byteValue = (byte) 0x50; 864 break; 865 case 0x36: // '6' 866 byteValue = (byte) 0x60; 867 break; 868 case 0x37: // '7' 869 byteValue = (byte) 0x70; 870 break; 871 case 0x38: // '8' 872 byteValue = (byte) 0x80; 873 break; 874 case 0x39: // '9' 875 byteValue = (byte) 0x90; 876 break; 877 case 0x41: // 'A' 878 case 0x61: // 'a' 879 byteValue = (byte) 0xA0; 880 break; 881 case 0x42: // 'B' 882 case 0x62: // 'b' 883 byteValue = (byte) 0xB0; 884 break; 885 case 0x43: // 'C' 886 case 0x63: // 'c' 887 byteValue = (byte) 0xC0; 888 break; 889 case 0x44: // 'D' 890 case 0x64: // 'd' 891 byteValue = (byte) 0xD0; 892 break; 893 case 0x45: // 'E' 894 case 0x65: // 'e' 895 byteValue = (byte) 0xE0; 896 break; 897 case 0x46: // 'F' 898 case 0x66: // 'f' 899 byteValue = (byte) 0xF0; 900 break; 901 default: 902 LocalizableMessage message = 903 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 904 get(filterString, equalPos+i+1); 905 throw new DirectoryException( 906 ResultCode.PROTOCOL_ERROR, message); 907 } 908 909 switch (valueBytes[++i]) 910 { 911 case 0x30: // '0' 912 break; 913 case 0x31: // '1' 914 byteValue |= (byte) 0x01; 915 break; 916 case 0x32: // '2' 917 byteValue |= (byte) 0x02; 918 break; 919 case 0x33: // '3' 920 byteValue |= (byte) 0x03; 921 break; 922 case 0x34: // '4' 923 byteValue |= (byte) 0x04; 924 break; 925 case 0x35: // '5' 926 byteValue |= (byte) 0x05; 927 break; 928 case 0x36: // '6' 929 byteValue |= (byte) 0x06; 930 break; 931 case 0x37: // '7' 932 byteValue |= (byte) 0x07; 933 break; 934 case 0x38: // '8' 935 byteValue |= (byte) 0x08; 936 break; 937 case 0x39: // '9' 938 byteValue |= (byte) 0x09; 939 break; 940 case 0x41: // 'A' 941 case 0x61: // 'a' 942 byteValue |= (byte) 0x0A; 943 break; 944 case 0x42: // 'B' 945 case 0x62: // 'b' 946 byteValue |= (byte) 0x0B; 947 break; 948 case 0x43: // 'C' 949 case 0x63: // 'c' 950 byteValue |= (byte) 0x0C; 951 break; 952 case 0x44: // 'D' 953 case 0x64: // 'd' 954 byteValue |= (byte) 0x0D; 955 break; 956 case 0x45: // 'E' 957 case 0x65: // 'e' 958 byteValue |= (byte) 0x0E; 959 break; 960 case 0x46: // 'F' 961 case 0x66: // 'f' 962 byteValue |= (byte) 0x0F; 963 break; 964 default: 965 LocalizableMessage message = 966 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 967 get(filterString, equalPos+i+1); 968 throw new DirectoryException( 969 ResultCode.PROTOCOL_ERROR, message); 970 } 971 972 valueBuffer.appendByte(byteValue); 973 } 974 else 975 { 976 valueBuffer.appendByte(valueBytes[i]); 977 } 978 } 979 980 userValue = valueBuffer.toByteString(); 981 } 982 else 983 { 984 userValue = ByteString.wrap(valueBytes); 985 } 986 987 return new SearchFilter(filterType, null, null, attributeType, 988 attributeOptions, userValue, null, null, 989 null, null, false); 990 } 991 } 992 993 994 995 /** 996 * Decodes a set of filters from the provided filter string within 997 * the indicated range. 998 * 999 * @param filterType The filter type for this compound filter. 1000 * It must be an AND, OR or NOT filter. 1001 * @param filterString The string containing the filter 1002 * information to decode. 1003 * @param startPos The position of the first character in the 1004 * set of filters to decode. 1005 * @param endPos The position of the first character after 1006 * the end of the set of filters to decode. 1007 * 1008 * @return The decoded search filter. 1009 * 1010 * @throws DirectoryException If a problem occurs while attempting 1011 * to decode the compound filter. 1012 */ 1013 private static SearchFilter decodeCompoundFilter( 1014 FilterType filterType, 1015 String filterString, int startPos, 1016 int endPos) 1017 throws DirectoryException 1018 { 1019 // Create a list to hold the returned components. 1020 List<SearchFilter> filterComponents = new ArrayList<>(); 1021 1022 1023 // If the end pos is equal to the start pos, then there are no components. 1024 if (startPos == endPos) 1025 { 1026 if (filterType == FilterType.NOT) 1027 { 1028 LocalizableMessage message = ERR_SEARCH_FILTER_NOT_EXACTLY_ONE.get( 1029 filterString, startPos, endPos); 1030 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 1031 } 1032 else 1033 { 1034 // This is valid and will be treated as a TRUE/FALSE filter. 1035 return new SearchFilter(filterType, filterComponents, null, 1036 null, null, null, null, null, null, 1037 null, false); 1038 } 1039 } 1040 1041 1042 // The first and last characters must be parentheses. If not, 1043 // then that's an error. 1044 if (filterString.charAt(startPos) != '(' || 1045 filterString.charAt(endPos-1) != ')') 1046 { 1047 LocalizableMessage message = 1048 ERR_SEARCH_FILTER_COMPOUND_MISSING_PARENTHESES. 1049 get(filterString, startPos, endPos); 1050 throw new DirectoryException( 1051 ResultCode.PROTOCOL_ERROR, message); 1052 } 1053 1054 1055 // Iterate through the characters in the value. Whenever an open 1056 // parenthesis is found, locate the corresponding close 1057 // parenthesis by counting the number of intermediate open/close 1058 // parentheses. 1059 int pendingOpens = 0; 1060 int openPos = -1; 1061 for (int i=startPos; i < endPos; i++) 1062 { 1063 char c = filterString.charAt(i); 1064 if (c == '(') 1065 { 1066 if (openPos < 0) 1067 { 1068 openPos = i; 1069 } 1070 1071 pendingOpens++; 1072 } 1073 else if (c == ')') 1074 { 1075 pendingOpens--; 1076 if (pendingOpens == 0) 1077 { 1078 filterComponents.add(createFilterFromString(filterString, 1079 openPos, i+1)); 1080 openPos = -1; 1081 } 1082 else if (pendingOpens < 0) 1083 { 1084 LocalizableMessage message = 1085 ERR_SEARCH_FILTER_NO_CORRESPONDING_OPEN_PARENTHESIS. 1086 get(filterString, i); 1087 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1088 message); 1089 } 1090 } 1091 else if (pendingOpens <= 0) 1092 { 1093 LocalizableMessage message = 1094 ERR_SEARCH_FILTER_COMPOUND_MISSING_PARENTHESES. 1095 get(filterString, startPos, endPos); 1096 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1097 message); 1098 } 1099 } 1100 1101 1102 // At this point, we have parsed the entire set of filter 1103 // components. The list of open parenthesis positions must be 1104 // empty. 1105 if (pendingOpens != 0) 1106 { 1107 LocalizableMessage message = 1108 ERR_SEARCH_FILTER_NO_CORRESPONDING_CLOSE_PARENTHESIS. 1109 get(filterString, openPos); 1110 throw new DirectoryException( 1111 ResultCode.PROTOCOL_ERROR, message); 1112 } 1113 1114 1115 // We should have everything we need, so return the list. 1116 if (filterType == FilterType.NOT) 1117 { 1118 if (filterComponents.size() != 1) 1119 { 1120 LocalizableMessage message = ERR_SEARCH_FILTER_NOT_EXACTLY_ONE.get( 1121 filterString, startPos, endPos); 1122 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1123 message); 1124 } 1125 SearchFilter notComponent = filterComponents.get(0); 1126 return new SearchFilter(filterType, null, notComponent, null, 1127 null, null, null, null, null, null, 1128 false); 1129 } 1130 else 1131 { 1132 return new SearchFilter(filterType, filterComponents, null, 1133 null, null, null, null, null, null, 1134 null, false); 1135 } 1136 } 1137 1138 1139 /** 1140 * Decodes a substring search filter component based on the provided 1141 * information. 1142 * 1143 * @param filterString The filter string containing the 1144 * information to decode. 1145 * @param attrType The attribute type for this substring 1146 * filter component. 1147 * @param options The set of attribute options for the 1148 * associated attribute type. 1149 * @param equalPos The location of the equal sign separating 1150 * the attribute type from the value. 1151 * @param endPos The position of the first character after 1152 * the end of the substring value. 1153 * 1154 * @return The decoded search filter. 1155 * 1156 * @throws DirectoryException If a problem occurs while attempting 1157 * to decode the substring filter. 1158 */ 1159 private static SearchFilter decodeSubstringFilter( 1160 String filterString, 1161 AttributeType attrType, 1162 Set<String> options, int equalPos, 1163 int endPos) 1164 throws DirectoryException 1165 { 1166 // Get a binary representation of the value. 1167 byte[] valueBytes = 1168 getBytes(filterString.substring(equalPos+1, endPos)); 1169 1170 1171 // Find the locations of all the asterisks in the value. Also, 1172 // check to see if there are any escaped values, since they will 1173 // need special treatment. 1174 boolean hasEscape = false; 1175 LinkedList<Integer> asteriskPositions = new LinkedList<>(); 1176 for (int i=0; i < valueBytes.length; i++) 1177 { 1178 if (valueBytes[i] == 0x2A) // The asterisk. 1179 { 1180 asteriskPositions.add(i); 1181 } 1182 else if (valueBytes[i] == 0x5C) // The backslash. 1183 { 1184 hasEscape = true; 1185 } 1186 } 1187 1188 1189 // If there were no asterisks, then this isn't a substring filter. 1190 if (asteriskPositions.isEmpty()) 1191 { 1192 LocalizableMessage message = ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get( 1193 filterString, equalPos+1, endPos); 1194 throw new DirectoryException( 1195 ResultCode.PROTOCOL_ERROR, message); 1196 } 1197 else 1198 { 1199 // The rest of the processing will be only on the value bytes, 1200 // so re-adjust the end position. 1201 endPos = valueBytes.length; 1202 } 1203 1204 1205 // If the value starts with an asterisk, then there is no 1206 // subInitial component. Otherwise, parse out the subInitial. 1207 ByteString subInitial; 1208 int firstPos = asteriskPositions.removeFirst(); 1209 if (firstPos == 0) 1210 { 1211 subInitial = null; 1212 } 1213 else 1214 { 1215 if (hasEscape) 1216 { 1217 ByteStringBuilder buffer = new ByteStringBuilder(firstPos); 1218 for (int i=0; i < firstPos; i++) 1219 { 1220 if (valueBytes[i] == 0x5C) 1221 { 1222 // The next two bytes must be the hex characters that 1223 // comprise the binary value. 1224 if (i + 2 >= valueBytes.length) 1225 { 1226 LocalizableMessage message = 1227 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1228 get(filterString, equalPos+i+1); 1229 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1230 message); 1231 } 1232 1233 byte byteValue = 0; 1234 switch (valueBytes[++i]) 1235 { 1236 case 0x30: // '0' 1237 break; 1238 case 0x31: // '1' 1239 byteValue = (byte) 0x10; 1240 break; 1241 case 0x32: // '2' 1242 byteValue = (byte) 0x20; 1243 break; 1244 case 0x33: // '3' 1245 byteValue = (byte) 0x30; 1246 break; 1247 case 0x34: // '4' 1248 byteValue = (byte) 0x40; 1249 break; 1250 case 0x35: // '5' 1251 byteValue = (byte) 0x50; 1252 break; 1253 case 0x36: // '6' 1254 byteValue = (byte) 0x60; 1255 break; 1256 case 0x37: // '7' 1257 byteValue = (byte) 0x70; 1258 break; 1259 case 0x38: // '8' 1260 byteValue = (byte) 0x80; 1261 break; 1262 case 0x39: // '9' 1263 byteValue = (byte) 0x90; 1264 break; 1265 case 0x41: // 'A' 1266 case 0x61: // 'a' 1267 byteValue = (byte) 0xA0; 1268 break; 1269 case 0x42: // 'B' 1270 case 0x62: // 'b' 1271 byteValue = (byte) 0xB0; 1272 break; 1273 case 0x43: // 'C' 1274 case 0x63: // 'c' 1275 byteValue = (byte) 0xC0; 1276 break; 1277 case 0x44: // 'D' 1278 case 0x64: // 'd' 1279 byteValue = (byte) 0xD0; 1280 break; 1281 case 0x45: // 'E' 1282 case 0x65: // 'e' 1283 byteValue = (byte) 0xE0; 1284 break; 1285 case 0x46: // 'F' 1286 case 0x66: // 'f' 1287 byteValue = (byte) 0xF0; 1288 break; 1289 default: 1290 LocalizableMessage message = 1291 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1292 get(filterString, equalPos+i+1); 1293 throw new DirectoryException( 1294 ResultCode.PROTOCOL_ERROR, message); 1295 } 1296 1297 switch (valueBytes[++i]) 1298 { 1299 case 0x30: // '0' 1300 break; 1301 case 0x31: // '1' 1302 byteValue |= (byte) 0x01; 1303 break; 1304 case 0x32: // '2' 1305 byteValue |= (byte) 0x02; 1306 break; 1307 case 0x33: // '3' 1308 byteValue |= (byte) 0x03; 1309 break; 1310 case 0x34: // '4' 1311 byteValue |= (byte) 0x04; 1312 break; 1313 case 0x35: // '5' 1314 byteValue |= (byte) 0x05; 1315 break; 1316 case 0x36: // '6' 1317 byteValue |= (byte) 0x06; 1318 break; 1319 case 0x37: // '7' 1320 byteValue |= (byte) 0x07; 1321 break; 1322 case 0x38: // '8' 1323 byteValue |= (byte) 0x08; 1324 break; 1325 case 0x39: // '9' 1326 byteValue |= (byte) 0x09; 1327 break; 1328 case 0x41: // 'A' 1329 case 0x61: // 'a' 1330 byteValue |= (byte) 0x0A; 1331 break; 1332 case 0x42: // 'B' 1333 case 0x62: // 'b' 1334 byteValue |= (byte) 0x0B; 1335 break; 1336 case 0x43: // 'C' 1337 case 0x63: // 'c' 1338 byteValue |= (byte) 0x0C; 1339 break; 1340 case 0x44: // 'D' 1341 case 0x64: // 'd' 1342 byteValue |= (byte) 0x0D; 1343 break; 1344 case 0x45: // 'E' 1345 case 0x65: // 'e' 1346 byteValue |= (byte) 0x0E; 1347 break; 1348 case 0x46: // 'F' 1349 case 0x66: // 'f' 1350 byteValue |= (byte) 0x0F; 1351 break; 1352 default: 1353 LocalizableMessage message = 1354 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1355 get(filterString, equalPos+i+1); 1356 throw new DirectoryException( 1357 ResultCode.PROTOCOL_ERROR, message); 1358 } 1359 1360 buffer.appendByte(byteValue); 1361 } 1362 else 1363 { 1364 buffer.appendByte(valueBytes[i]); 1365 } 1366 } 1367 1368 subInitial = buffer.toByteString(); 1369 } 1370 else 1371 { 1372 subInitial = ByteString.wrap(valueBytes, 0, firstPos); 1373 } 1374 } 1375 1376 1377 // Next, process through the rest of the asterisks to get the subAny values. 1378 List<ByteString> subAny = new ArrayList<>(); 1379 for (int asteriskPos : asteriskPositions) 1380 { 1381 int length = asteriskPos - firstPos - 1; 1382 1383 if (hasEscape) 1384 { 1385 ByteStringBuilder buffer = new ByteStringBuilder(length); 1386 for (int i=firstPos+1; i < asteriskPos; i++) 1387 { 1388 if (valueBytes[i] == 0x5C) 1389 { 1390 // The next two bytes must be the hex characters that 1391 // comprise the binary value. 1392 if (i + 2 >= valueBytes.length) 1393 { 1394 LocalizableMessage message = 1395 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1396 get(filterString, equalPos+i+1); 1397 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1398 message); 1399 } 1400 1401 byte byteValue = 0; 1402 switch (valueBytes[++i]) 1403 { 1404 case 0x30: // '0' 1405 break; 1406 case 0x31: // '1' 1407 byteValue = (byte) 0x10; 1408 break; 1409 case 0x32: // '2' 1410 byteValue = (byte) 0x20; 1411 break; 1412 case 0x33: // '3' 1413 byteValue = (byte) 0x30; 1414 break; 1415 case 0x34: // '4' 1416 byteValue = (byte) 0x40; 1417 break; 1418 case 0x35: // '5' 1419 byteValue = (byte) 0x50; 1420 break; 1421 case 0x36: // '6' 1422 byteValue = (byte) 0x60; 1423 break; 1424 case 0x37: // '7' 1425 byteValue = (byte) 0x70; 1426 break; 1427 case 0x38: // '8' 1428 byteValue = (byte) 0x80; 1429 break; 1430 case 0x39: // '9' 1431 byteValue = (byte) 0x90; 1432 break; 1433 case 0x41: // 'A' 1434 case 0x61: // 'a' 1435 byteValue = (byte) 0xA0; 1436 break; 1437 case 0x42: // 'B' 1438 case 0x62: // 'b' 1439 byteValue = (byte) 0xB0; 1440 break; 1441 case 0x43: // 'C' 1442 case 0x63: // 'c' 1443 byteValue = (byte) 0xC0; 1444 break; 1445 case 0x44: // 'D' 1446 case 0x64: // 'd' 1447 byteValue = (byte) 0xD0; 1448 break; 1449 case 0x45: // 'E' 1450 case 0x65: // 'e' 1451 byteValue = (byte) 0xE0; 1452 break; 1453 case 0x46: // 'F' 1454 case 0x66: // 'f' 1455 byteValue = (byte) 0xF0; 1456 break; 1457 default: 1458 LocalizableMessage message = 1459 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1460 get(filterString, equalPos+i+1); 1461 throw new DirectoryException( 1462 ResultCode.PROTOCOL_ERROR, message); 1463 } 1464 1465 switch (valueBytes[++i]) 1466 { 1467 case 0x30: // '0' 1468 break; 1469 case 0x31: // '1' 1470 byteValue |= (byte) 0x01; 1471 break; 1472 case 0x32: // '2' 1473 byteValue |= (byte) 0x02; 1474 break; 1475 case 0x33: // '3' 1476 byteValue |= (byte) 0x03; 1477 break; 1478 case 0x34: // '4' 1479 byteValue |= (byte) 0x04; 1480 break; 1481 case 0x35: // '5' 1482 byteValue |= (byte) 0x05; 1483 break; 1484 case 0x36: // '6' 1485 byteValue |= (byte) 0x06; 1486 break; 1487 case 0x37: // '7' 1488 byteValue |= (byte) 0x07; 1489 break; 1490 case 0x38: // '8' 1491 byteValue |= (byte) 0x08; 1492 break; 1493 case 0x39: // '9' 1494 byteValue |= (byte) 0x09; 1495 break; 1496 case 0x41: // 'A' 1497 case 0x61: // 'a' 1498 byteValue |= (byte) 0x0A; 1499 break; 1500 case 0x42: // 'B' 1501 case 0x62: // 'b' 1502 byteValue |= (byte) 0x0B; 1503 break; 1504 case 0x43: // 'C' 1505 case 0x63: // 'c' 1506 byteValue |= (byte) 0x0C; 1507 break; 1508 case 0x44: // 'D' 1509 case 0x64: // 'd' 1510 byteValue |= (byte) 0x0D; 1511 break; 1512 case 0x45: // 'E' 1513 case 0x65: // 'e' 1514 byteValue |= (byte) 0x0E; 1515 break; 1516 case 0x46: // 'F' 1517 case 0x66: // 'f' 1518 byteValue |= (byte) 0x0F; 1519 break; 1520 default: 1521 LocalizableMessage message = 1522 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1523 get(filterString, equalPos+i+1); 1524 throw new DirectoryException( 1525 ResultCode.PROTOCOL_ERROR, message); 1526 } 1527 1528 buffer.appendByte(byteValue); 1529 } 1530 else 1531 { 1532 buffer.appendByte(valueBytes[i]); 1533 } 1534 } 1535 1536 subAny.add(buffer.toByteString()); 1537 buffer.clear(); 1538 } 1539 else 1540 { 1541 subAny.add(ByteString.wrap(valueBytes, firstPos+1, length)); 1542 } 1543 1544 1545 firstPos = asteriskPos; 1546 } 1547 1548 1549 // Finally, see if there is anything after the last asterisk, 1550 // which would be the subFinal value. 1551 ByteString subFinal; 1552 if (firstPos == (endPos-1)) 1553 { 1554 subFinal = null; 1555 } 1556 else 1557 { 1558 int length = endPos - firstPos - 1; 1559 1560 if (hasEscape) 1561 { 1562 ByteStringBuilder buffer = new ByteStringBuilder(length); 1563 for (int i=firstPos+1; i < endPos; i++) 1564 { 1565 if (valueBytes[i] == 0x5C) 1566 { 1567 // The next two bytes must be the hex characters that 1568 // comprise the binary value. 1569 if (i + 2 >= valueBytes.length) 1570 { 1571 LocalizableMessage message = 1572 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1573 get(filterString, equalPos+i+1); 1574 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1575 message); 1576 } 1577 1578 byte byteValue = 0; 1579 switch (valueBytes[++i]) 1580 { 1581 case 0x30: // '0' 1582 break; 1583 case 0x31: // '1' 1584 byteValue = (byte) 0x10; 1585 break; 1586 case 0x32: // '2' 1587 byteValue = (byte) 0x20; 1588 break; 1589 case 0x33: // '3' 1590 byteValue = (byte) 0x30; 1591 break; 1592 case 0x34: // '4' 1593 byteValue = (byte) 0x40; 1594 break; 1595 case 0x35: // '5' 1596 byteValue = (byte) 0x50; 1597 break; 1598 case 0x36: // '6' 1599 byteValue = (byte) 0x60; 1600 break; 1601 case 0x37: // '7' 1602 byteValue = (byte) 0x70; 1603 break; 1604 case 0x38: // '8' 1605 byteValue = (byte) 0x80; 1606 break; 1607 case 0x39: // '9' 1608 byteValue = (byte) 0x90; 1609 break; 1610 case 0x41: // 'A' 1611 case 0x61: // 'a' 1612 byteValue = (byte) 0xA0; 1613 break; 1614 case 0x42: // 'B' 1615 case 0x62: // 'b' 1616 byteValue = (byte) 0xB0; 1617 break; 1618 case 0x43: // 'C' 1619 case 0x63: // 'c' 1620 byteValue = (byte) 0xC0; 1621 break; 1622 case 0x44: // 'D' 1623 case 0x64: // 'd' 1624 byteValue = (byte) 0xD0; 1625 break; 1626 case 0x45: // 'E' 1627 case 0x65: // 'e' 1628 byteValue = (byte) 0xE0; 1629 break; 1630 case 0x46: // 'F' 1631 case 0x66: // 'f' 1632 byteValue = (byte) 0xF0; 1633 break; 1634 default: 1635 LocalizableMessage message = 1636 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1637 get(filterString, equalPos+i+1); 1638 throw new DirectoryException( 1639 ResultCode.PROTOCOL_ERROR, message); 1640 } 1641 1642 switch (valueBytes[++i]) 1643 { 1644 case 0x30: // '0' 1645 break; 1646 case 0x31: // '1' 1647 byteValue |= (byte) 0x01; 1648 break; 1649 case 0x32: // '2' 1650 byteValue |= (byte) 0x02; 1651 break; 1652 case 0x33: // '3' 1653 byteValue |= (byte) 0x03; 1654 break; 1655 case 0x34: // '4' 1656 byteValue |= (byte) 0x04; 1657 break; 1658 case 0x35: // '5' 1659 byteValue |= (byte) 0x05; 1660 break; 1661 case 0x36: // '6' 1662 byteValue |= (byte) 0x06; 1663 break; 1664 case 0x37: // '7' 1665 byteValue |= (byte) 0x07; 1666 break; 1667 case 0x38: // '8' 1668 byteValue |= (byte) 0x08; 1669 break; 1670 case 0x39: // '9' 1671 byteValue |= (byte) 0x09; 1672 break; 1673 case 0x41: // 'A' 1674 case 0x61: // 'a' 1675 byteValue |= (byte) 0x0A; 1676 break; 1677 case 0x42: // 'B' 1678 case 0x62: // 'b' 1679 byteValue |= (byte) 0x0B; 1680 break; 1681 case 0x43: // 'C' 1682 case 0x63: // 'c' 1683 byteValue |= (byte) 0x0C; 1684 break; 1685 case 0x44: // 'D' 1686 case 0x64: // 'd' 1687 byteValue |= (byte) 0x0D; 1688 break; 1689 case 0x45: // 'E' 1690 case 0x65: // 'e' 1691 byteValue |= (byte) 0x0E; 1692 break; 1693 case 0x46: // 'F' 1694 case 0x66: // 'f' 1695 byteValue |= (byte) 0x0F; 1696 break; 1697 default: 1698 LocalizableMessage message = 1699 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1700 get(filterString, equalPos+i+1); 1701 throw new DirectoryException( 1702 ResultCode.PROTOCOL_ERROR, message); 1703 } 1704 1705 buffer.appendByte(byteValue); 1706 } 1707 else 1708 { 1709 buffer.appendByte(valueBytes[i]); 1710 } 1711 } 1712 1713 subFinal = buffer.toByteString(); 1714 } 1715 else 1716 { 1717 subFinal = ByteString.wrap(valueBytes, firstPos+1, length); 1718 } 1719 } 1720 1721 1722 return new SearchFilter(FilterType.SUBSTRING, null, null, 1723 attrType, options, null, subInitial, 1724 subAny, subFinal, null, false); 1725 } 1726 1727 1728 1729 /** 1730 * Decodes an extensible match filter component based on the 1731 * provided information. 1732 * 1733 * @param filterString The filter string containing the 1734 * information to decode. 1735 * @param startPos The position in the filter string of the 1736 * first character in the extensible match 1737 * filter. 1738 * @param equalPos The position of the equal sign in the 1739 * extensible match filter. 1740 * @param endPos The position of the first character after 1741 * the end of the extensible match filter. 1742 * 1743 * @return The decoded search filter. 1744 * 1745 * @throws DirectoryException If a problem occurs while attempting 1746 * to decode the extensible match 1747 * filter. 1748 */ 1749 private static SearchFilter decodeExtensibleMatchFilter( 1750 String filterString, int startPos, 1751 int equalPos, int endPos) 1752 throws DirectoryException 1753 { 1754 AttributeType attributeType = null; 1755 Set<String> attributeOptions = new HashSet<>(); 1756 boolean dnAttributes = false; 1757 String matchingRuleID = null; 1758 1759 1760 // Look at the first character. If it is a colon, then it must be 1761 // followed by either the string "dn" or the matching rule ID. If 1762 // it is not, then it must be the attribute type. 1763 String lowerLeftStr = 1764 toLowerCase(filterString.substring(startPos, equalPos)); 1765 if (filterString.charAt(startPos) == ':') 1766 { 1767 // See if it starts with ":dn". Otherwise, it much be the 1768 // matching rule 1769 // ID. 1770 if (lowerLeftStr.startsWith(":dn:")) 1771 { 1772 dnAttributes = true; 1773 1774 matchingRuleID = 1775 filterString.substring(startPos+4, equalPos-1); 1776 } 1777 else 1778 { 1779 matchingRuleID = 1780 filterString.substring(startPos+1, equalPos-1); 1781 } 1782 } 1783 else 1784 { 1785 int colonPos = filterString.indexOf(':',startPos); 1786 if (colonPos < 0) 1787 { 1788 LocalizableMessage message = ERR_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_COLON. 1789 get(filterString, startPos); 1790 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1791 message); 1792 } 1793 1794 1795 String attrType = filterString.substring(startPos, colonPos); 1796 StringBuilder lowerType = new StringBuilder(attrType.length()); 1797 1798 int semicolonPos = attrType.indexOf(';'); 1799 if (semicolonPos <0) 1800 { 1801 for (int i=0; i < attrType.length(); i++) 1802 { 1803 lowerType.append(Character.toLowerCase(attrType.charAt(i))); 1804 } 1805 } 1806 else 1807 { 1808 for (int i=0; i < semicolonPos; i++) 1809 { 1810 lowerType.append(Character.toLowerCase(attrType.charAt(i))); 1811 } 1812 1813 int nextPos = attrType.indexOf(';', semicolonPos+1); 1814 while (nextPos > 0) 1815 { 1816 attributeOptions.add(attrType.substring(semicolonPos+1, 1817 nextPos)); 1818 semicolonPos = nextPos; 1819 nextPos = attrType.indexOf(';', semicolonPos+1); 1820 } 1821 1822 attributeOptions.add(attrType.substring(semicolonPos+1)); 1823 } 1824 1825 1826 // Get the attribute type for the specified name. 1827 attributeType = getAttributeType(attrType, lowerType); 1828 1829 // If there is anything left, then it should be ":dn" and/or ":" 1830 // followed by the matching rule ID. 1831 if (colonPos < equalPos-1) 1832 { 1833 if (lowerLeftStr.startsWith(":dn:", colonPos)) 1834 { 1835 dnAttributes = true; 1836 1837 if (colonPos+4 < equalPos-1) 1838 { 1839 matchingRuleID = 1840 filterString.substring(colonPos+4, equalPos-1); 1841 } 1842 } 1843 else 1844 { 1845 matchingRuleID = 1846 filterString.substring(colonPos+1, equalPos-1); 1847 } 1848 } 1849 } 1850 1851 1852 // Parse out the attribute value. 1853 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, 1854 endPos)); 1855 boolean hasEscape = false; 1856 for (byte valueByte : valueBytes) 1857 { 1858 if (valueByte == 0x5C) 1859 { 1860 hasEscape = true; 1861 break; 1862 } 1863 } 1864 1865 ByteString userValue; 1866 if (hasEscape) 1867 { 1868 ByteStringBuilder valueBuffer = 1869 new ByteStringBuilder(valueBytes.length); 1870 for (int i=0; i < valueBytes.length; i++) 1871 { 1872 if (valueBytes[i] == 0x5C) // The backslash character 1873 { 1874 // The next two bytes must be the hex characters that 1875 // comprise the binary value. 1876 if (i + 2 >= valueBytes.length) 1877 { 1878 LocalizableMessage message = ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1879 get(filterString, equalPos+i+1); 1880 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1881 message); 1882 } 1883 1884 byte byteValue = 0; 1885 switch (valueBytes[++i]) 1886 { 1887 case 0x30: // '0' 1888 break; 1889 case 0x31: // '1' 1890 byteValue = (byte) 0x10; 1891 break; 1892 case 0x32: // '2' 1893 byteValue = (byte) 0x20; 1894 break; 1895 case 0x33: // '3' 1896 byteValue = (byte) 0x30; 1897 break; 1898 case 0x34: // '4' 1899 byteValue = (byte) 0x40; 1900 break; 1901 case 0x35: // '5' 1902 byteValue = (byte) 0x50; 1903 break; 1904 case 0x36: // '6' 1905 byteValue = (byte) 0x60; 1906 break; 1907 case 0x37: // '7' 1908 byteValue = (byte) 0x70; 1909 break; 1910 case 0x38: // '8' 1911 byteValue = (byte) 0x80; 1912 break; 1913 case 0x39: // '9' 1914 byteValue = (byte) 0x90; 1915 break; 1916 case 0x41: // 'A' 1917 case 0x61: // 'a' 1918 byteValue = (byte) 0xA0; 1919 break; 1920 case 0x42: // 'B' 1921 case 0x62: // 'b' 1922 byteValue = (byte) 0xB0; 1923 break; 1924 case 0x43: // 'C' 1925 case 0x63: // 'c' 1926 byteValue = (byte) 0xC0; 1927 break; 1928 case 0x44: // 'D' 1929 case 0x64: // 'd' 1930 byteValue = (byte) 0xD0; 1931 break; 1932 case 0x45: // 'E' 1933 case 0x65: // 'e' 1934 byteValue = (byte) 0xE0; 1935 break; 1936 case 0x46: // 'F' 1937 case 0x66: // 'f' 1938 byteValue = (byte) 0xF0; 1939 break; 1940 default: 1941 LocalizableMessage message = 1942 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 1943 get(filterString, equalPos+i+1); 1944 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1945 message); 1946 } 1947 1948 switch (valueBytes[++i]) 1949 { 1950 case 0x30: // '0' 1951 break; 1952 case 0x31: // '1' 1953 byteValue |= (byte) 0x01; 1954 break; 1955 case 0x32: // '2' 1956 byteValue |= (byte) 0x02; 1957 break; 1958 case 0x33: // '3' 1959 byteValue |= (byte) 0x03; 1960 break; 1961 case 0x34: // '4' 1962 byteValue |= (byte) 0x04; 1963 break; 1964 case 0x35: // '5' 1965 byteValue |= (byte) 0x05; 1966 break; 1967 case 0x36: // '6' 1968 byteValue |= (byte) 0x06; 1969 break; 1970 case 0x37: // '7' 1971 byteValue |= (byte) 0x07; 1972 break; 1973 case 0x38: // '8' 1974 byteValue |= (byte) 0x08; 1975 break; 1976 case 0x39: // '9' 1977 byteValue |= (byte) 0x09; 1978 break; 1979 case 0x41: // 'A' 1980 case 0x61: // 'a' 1981 byteValue |= (byte) 0x0A; 1982 break; 1983 case 0x42: // 'B' 1984 case 0x62: // 'b' 1985 byteValue |= (byte) 0x0B; 1986 break; 1987 case 0x43: // 'C' 1988 case 0x63: // 'c' 1989 byteValue |= (byte) 0x0C; 1990 break; 1991 case 0x44: // 'D' 1992 case 0x64: // 'd' 1993 byteValue |= (byte) 0x0D; 1994 break; 1995 case 0x45: // 'E' 1996 case 0x65: // 'e' 1997 byteValue |= (byte) 0x0E; 1998 break; 1999 case 0x46: // 'F' 2000 case 0x66: // 'f' 2001 byteValue |= (byte) 0x0F; 2002 break; 2003 default: 2004 LocalizableMessage message = 2005 ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE. 2006 get(filterString, equalPos+i+1); 2007 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 2008 message); 2009 } 2010 2011 valueBuffer.appendByte(byteValue); 2012 } 2013 else 2014 { 2015 valueBuffer.appendByte(valueBytes[i]); 2016 } 2017 } 2018 2019 userValue = valueBuffer.toByteString(); 2020 } 2021 else 2022 { 2023 userValue = ByteString.wrap(valueBytes); 2024 } 2025 2026 // Make sure that the filter contains at least one of an attribute 2027 // type or a matching rule ID. Also, construct the appropriate 2028 // attribute value. 2029 if (attributeType == null) 2030 { 2031 if (matchingRuleID == null) 2032 { 2033 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 2034 ERR_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR.get(filterString, startPos)); 2035 } 2036 2037 MatchingRule mr = DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID)); 2038 if (mr == null) 2039 { 2040 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 2041 ERR_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_SUCH_MR.get(filterString, startPos, matchingRuleID)); 2042 } 2043 } 2044 2045 return new SearchFilter(FilterType.EXTENSIBLE_MATCH, null, null, 2046 attributeType, attributeOptions, userValue, 2047 null, null, null, matchingRuleID, 2048 dnAttributes); 2049 } 2050 2051 private static AttributeType getAttributeType(String attrType, StringBuilder lowerType) 2052 { 2053 AttributeType attributeType = DirectoryServer.getAttributeType(lowerType.toString()); 2054 if (attributeType.isPlaceHolder()) 2055 { 2056 String typeStr = attrType.substring(0, lowerType.length()); 2057 attributeType = DirectoryServer.getAttributeType(typeStr); 2058 } 2059 return attributeType; 2060 } 2061 2062 /** 2063 * Retrieves the filter type for this search filter. 2064 * 2065 * @return The filter type for this search filter. 2066 */ 2067 public FilterType getFilterType() 2068 { 2069 return filterType; 2070 } 2071 2072 2073 2074 /** 2075 * Retrieves the set of filter components for this AND or OR filter. 2076 * The returned list can be modified by the caller. 2077 * 2078 * @return The set of filter components for this AND or OR filter. 2079 */ 2080 public Set<SearchFilter> getFilterComponents() 2081 { 2082 return filterComponents; 2083 } 2084 2085 2086 2087 /** 2088 * Retrieves the filter component for this NOT filter. 2089 * 2090 * @return The filter component for this NOT filter, or 2091 * <CODE>null</CODE> if this is not a NOT filter. 2092 */ 2093 public SearchFilter getNotComponent() 2094 { 2095 return notComponent; 2096 } 2097 2098 2099 2100 /** 2101 * Retrieves the attribute type for this filter. 2102 * 2103 * @return The attribute type for this filter, or <CODE>null</CODE> 2104 * if there is none. 2105 */ 2106 public AttributeType getAttributeType() 2107 { 2108 return attributeType; 2109 } 2110 2111 2112 2113 /** 2114 * Retrieves the assertion value for this filter. 2115 * 2116 * @return The assertion value for this filter, or 2117 * <CODE>null</CODE> if there is none. 2118 */ 2119 public ByteString getAssertionValue() 2120 { 2121 return assertionValue; 2122 } 2123 2124 /** 2125 * Retrieves the subInitial element for this substring filter. 2126 * 2127 * @return The subInitial element for this substring filter, or 2128 * <CODE>null</CODE> if there is none. 2129 */ 2130 public ByteString getSubInitialElement() 2131 { 2132 return subInitialElement; 2133 } 2134 2135 2136 2137 /** 2138 * Retrieves the set of subAny elements for this substring filter. 2139 * The returned list may be altered by the caller. 2140 * 2141 * @return The set of subAny elements for this substring filter. 2142 */ 2143 public List<ByteString> getSubAnyElements() 2144 { 2145 return subAnyElements; 2146 } 2147 2148 2149 2150 /** 2151 * Retrieves the subFinal element for this substring filter. 2152 * 2153 * @return The subFinal element for this substring filter. 2154 */ 2155 public ByteString getSubFinalElement() 2156 { 2157 return subFinalElement; 2158 } 2159 2160 2161 2162 /** 2163 * Retrieves the matching rule ID for this extensible matching 2164 * filter. 2165 * 2166 * @return The matching rule ID for this extensible matching 2167 * filter. 2168 */ 2169 public String getMatchingRuleID() 2170 { 2171 return matchingRuleID; 2172 } 2173 2174 2175 2176 /** 2177 * Retrieves the dnAttributes flag for this extensible matching 2178 * filter. 2179 * 2180 * @return The dnAttributes flag for this extensible matching 2181 * filter. 2182 */ 2183 public boolean getDNAttributes() 2184 { 2185 return dnAttributes; 2186 } 2187 2188 2189 2190 /** 2191 * Indicates whether this search filter matches the provided entry. 2192 * 2193 * @param entry The entry for which to make the determination. 2194 * 2195 * @return <CODE>true</CODE> if this search filter matches the 2196 * provided entry, or <CODE>false</CODE> if it does not. 2197 * 2198 * @throws DirectoryException If a problem is encountered during 2199 * processing. 2200 */ 2201 public boolean matchesEntry(Entry entry) 2202 throws DirectoryException 2203 { 2204 ConditionResult result = matchesEntryInternal(this, entry, 0); 2205 switch (result) 2206 { 2207 case TRUE: 2208 return true; 2209 case FALSE: 2210 case UNDEFINED: 2211 return false; 2212 default: 2213 logger.error(ERR_SEARCH_FILTER_INVALID_RESULT_TYPE, entry.getName(), this, result); 2214 return false; 2215 } 2216 } 2217 2218 2219 2220 /** 2221 * Indicates whether the this filter matches the provided entry. 2222 * 2223 * @param completeFilter The complete filter being checked, of 2224 * which this filter may be a subset. 2225 * @param entry The entry for which to make the 2226 * determination. 2227 * @param depth The current depth of the evaluation, 2228 * which is used to prevent infinite 2229 * recursion due to highly nested filters 2230 * and eventually running out of stack 2231 * space. 2232 * 2233 * @return <CODE>TRUE</CODE> if this filter matches the provided 2234 * entry, <CODE>FALSE</CODE> if it does not, or 2235 * <CODE>UNDEFINED</CODE> if the result is undefined. 2236 * 2237 * @throws DirectoryException If a problem is encountered during 2238 * processing. 2239 */ 2240 private ConditionResult matchesEntryInternal( 2241 SearchFilter completeFilter, 2242 Entry entry, int depth) 2243 throws DirectoryException 2244 { 2245 switch (filterType) 2246 { 2247 case AND: 2248 return processAND(completeFilter, entry, depth); 2249 2250 case OR: 2251 return processOR(completeFilter, entry, depth); 2252 2253 case NOT: 2254 return processNOT(completeFilter, entry, depth); 2255 2256 case EQUALITY: 2257 return processEquality(completeFilter, entry); 2258 2259 case SUBSTRING: 2260 return processSubstring(completeFilter, entry); 2261 2262 case GREATER_OR_EQUAL: 2263 return processGreaterOrEqual(completeFilter, entry); 2264 2265 case LESS_OR_EQUAL: 2266 return processLessOrEqual(completeFilter, entry); 2267 2268 case PRESENT: 2269 return processPresent(completeFilter, entry); 2270 2271 case APPROXIMATE_MATCH: 2272 return processApproximate(completeFilter, entry); 2273 2274 case EXTENSIBLE_MATCH: 2275 return processExtensibleMatch(completeFilter, entry); 2276 2277 2278 default: 2279 // This is an invalid filter type. 2280 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 2281 ERR_SEARCH_FILTER_INVALID_FILTER_TYPE.get(entry.getName(), this, filterType)); 2282 } 2283 } 2284 2285 2286 2287 /** 2288 * Indicates whether the this AND filter matches the provided entry. 2289 * 2290 * @param completeFilter The complete filter being checked, of 2291 * which this filter may be a subset. 2292 * @param entry The entry for which to make the 2293 * determination. 2294 * @param depth The current depth of the evaluation, 2295 * which is used to prevent infinite 2296 * recursion due to highly nested filters 2297 * and eventually running out of stack 2298 * space. 2299 * 2300 * @return <CODE>TRUE</CODE> if this filter matches the provided 2301 * entry, <CODE>FALSE</CODE> if it does not, or 2302 * <CODE>UNDEFINED</CODE> if the result is undefined. 2303 * 2304 * @throws DirectoryException If a problem is encountered during 2305 * processing. 2306 */ 2307 private ConditionResult processAND(SearchFilter completeFilter, 2308 Entry entry, int depth) 2309 throws DirectoryException 2310 { 2311 if (filterComponents == null) 2312 { 2313 // The set of subcomponents was null. This is not allowed. 2314 LocalizableMessage message = 2315 ERR_SEARCH_FILTER_COMPOUND_COMPONENTS_NULL. 2316 get(entry.getName(), completeFilter, filterType); 2317 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 2318 } 2319 else if (filterComponents.isEmpty()) 2320 { 2321 // An AND filter with no elements like "(&)" is specified as 2322 // "undefined" in RFC 2251, but is considered one of the 2323 // TRUE/FALSE filters in RFC 4526, in which case we should 2324 // always return true. 2325 if (logger.isTraceEnabled()) 2326 { 2327 logger.trace("Returning TRUE for LDAP TRUE " + 2328 "filter (&)"); 2329 } 2330 return ConditionResult.TRUE; 2331 } 2332 else 2333 { 2334 // We will have to evaluate one or more subcomponents. In 2335 // this case, first check our depth to make sure we're not 2336 // nesting too deep. 2337 if (depth >= MAX_NESTED_FILTER_DEPTH) 2338 { 2339 LocalizableMessage message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP. 2340 get(entry.getName(), completeFilter); 2341 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 2342 } 2343 2344 for (SearchFilter f : filterComponents) 2345 { 2346 ConditionResult result = 2347 f.matchesEntryInternal(completeFilter, entry, depth + 1); 2348 switch (result) 2349 { 2350 case TRUE: 2351 break; 2352 case FALSE: 2353 if (logger.isTraceEnabled()) 2354 { 2355 logger.trace( 2356 "Returning FALSE for AND component %s in " + 2357 "filter %s for entry %s", 2358 f, completeFilter, entry.getName()); 2359 } 2360 return result; 2361 case UNDEFINED: 2362 if (logger.isTraceEnabled()) 2363 { 2364 logger.trace( 2365 "Undefined result for AND component %s in filter " + 2366 "%s for entry %s", f, completeFilter, entry.getName()); 2367 } 2368 return result; 2369 default: 2370 LocalizableMessage message = 2371 ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 2372 get(entry.getName(), completeFilter, result); 2373 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 2374 } 2375 } 2376 2377 // If we have gotten here, then all the components must have 2378 // matched. 2379 if (logger.isTraceEnabled()) 2380 { 2381 logger.trace( 2382 "Returning TRUE for AND component %s in filter %s " + 2383 "for entry %s", this, completeFilter, entry.getName()); 2384 } 2385 return ConditionResult.TRUE; 2386 } 2387 } 2388 2389 2390 2391 /** 2392 * Indicates whether the this OR filter matches the provided entry. 2393 * 2394 * @param completeFilter The complete filter being checked, of 2395 * which this filter may be a subset. 2396 * @param entry The entry for which to make the 2397 * determination. 2398 * @param depth The current depth of the evaluation, 2399 * which is used to prevent infinite 2400 * recursion due to highly nested filters 2401 * and eventually running out of stack 2402 * space. 2403 * 2404 * @return <CODE>TRUE</CODE> if this filter matches the provided 2405 * entry, <CODE>FALSE</CODE> if it does not, or 2406 * <CODE>UNDEFINED</CODE> if the result is undefined. 2407 * 2408 * @throws DirectoryException If a problem is encountered during 2409 * processing. 2410 */ 2411 private ConditionResult processOR(SearchFilter completeFilter, 2412 Entry entry, int depth) 2413 throws DirectoryException 2414 { 2415 if (filterComponents == null) 2416 { 2417 // The set of subcomponents was null. This is not allowed. 2418 LocalizableMessage message = 2419 ERR_SEARCH_FILTER_COMPOUND_COMPONENTS_NULL. 2420 get(entry.getName(), completeFilter, filterType); 2421 throw new DirectoryException( 2422 DirectoryServer.getServerErrorResultCode(), 2423 message); 2424 } 2425 else if (filterComponents.isEmpty()) 2426 { 2427 // An OR filter with no elements like "(|)" is specified as 2428 // "undefined" in RFC 2251, but is considered one of the 2429 // TRUE/FALSE filters in RFC 4526, in which case we should 2430 // always return false. 2431 if (logger.isTraceEnabled()) 2432 { 2433 logger.trace("Returning FALSE for LDAP FALSE " + 2434 "filter (|)"); 2435 } 2436 return ConditionResult.FALSE; 2437 } 2438 else 2439 { 2440 // We will have to evaluate one or more subcomponents. In 2441 // this case, first check our depth to make sure we're not 2442 // nesting too deep. 2443 if (depth >= MAX_NESTED_FILTER_DEPTH) 2444 { 2445 LocalizableMessage message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP. 2446 get(entry.getName(), completeFilter); 2447 throw new DirectoryException( 2448 DirectoryServer.getServerErrorResultCode(), 2449 message); 2450 } 2451 2452 ConditionResult result = ConditionResult.FALSE; 2453 for (SearchFilter f : filterComponents) 2454 { 2455 switch (f.matchesEntryInternal(completeFilter, entry, 2456 depth+1)) 2457 { 2458 case TRUE: 2459 if (logger.isTraceEnabled()) 2460 { 2461 logger.trace( 2462 "Returning TRUE for OR component %s in filter " + 2463 "%s for entry %s", 2464 f, completeFilter, entry.getName()); 2465 } 2466 return ConditionResult.TRUE; 2467 case FALSE: 2468 break; 2469 case UNDEFINED: 2470 if (logger.isTraceEnabled()) 2471 { 2472 logger.trace( 2473 "Undefined result for OR component %s in filter " + 2474 "%s for entry %s", 2475 f, completeFilter, entry.getName()); 2476 } 2477 result = ConditionResult.UNDEFINED; 2478 break; 2479 default: 2480 LocalizableMessage message = 2481 ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 2482 get(entry.getName(), completeFilter, result); 2483 throw new 2484 DirectoryException( 2485 DirectoryServer.getServerErrorResultCode(), 2486 message); 2487 } 2488 } 2489 2490 2491 if (logger.isTraceEnabled()) 2492 { 2493 logger.trace( 2494 "Returning %s for OR component %s in filter %s for " + 2495 "entry %s", result, this, completeFilter, 2496 entry.getName()); 2497 } 2498 return result; 2499 } 2500 } 2501 2502 2503 2504 /** 2505 * Indicates whether the this NOT filter matches the provided entry. 2506 * 2507 * @param completeFilter The complete filter being checked, of 2508 * which this filter may be a subset. 2509 * @param entry The entry for which to make the 2510 * determination. 2511 * @param depth The current depth of the evaluation, 2512 * which is used to prevent infinite 2513 * recursion due to highly nested filters 2514 * and eventually running out of stack 2515 * space. 2516 * 2517 * @return <CODE>TRUE</CODE> if this filter matches the provided 2518 * entry, <CODE>FALSE</CODE> if it does not, or 2519 * <CODE>UNDEFINED</CODE> if the result is undefined. 2520 * 2521 * @throws DirectoryException If a problem is encountered during 2522 * processing. 2523 */ 2524 private ConditionResult processNOT(SearchFilter completeFilter, 2525 Entry entry, int depth) 2526 throws DirectoryException 2527 { 2528 if (notComponent == null) 2529 { 2530 // The NOT subcomponent was null. This is not allowed. 2531 LocalizableMessage message = ERR_SEARCH_FILTER_NOT_COMPONENT_NULL. 2532 get(entry.getName(), completeFilter); 2533 throw new DirectoryException( 2534 DirectoryServer.getServerErrorResultCode(), 2535 message); 2536 } 2537 else 2538 { 2539 // The subcomponent for the NOT filter can be an AND, OR, or 2540 // NOT filter that would require more nesting. Make sure 2541 // that we don't go too deep. 2542 if (depth >= MAX_NESTED_FILTER_DEPTH) 2543 { 2544 LocalizableMessage message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP. 2545 get(entry.getName(), completeFilter); 2546 throw new DirectoryException( 2547 DirectoryServer.getServerErrorResultCode(), 2548 message); 2549 } 2550 2551 ConditionResult result = 2552 notComponent.matchesEntryInternal(completeFilter, 2553 entry, depth+1); 2554 switch (result) 2555 { 2556 case TRUE: 2557 if (logger.isTraceEnabled()) 2558 { 2559 logger.trace( 2560 "Returning FALSE for NOT component %s in filter " + 2561 "%s for entry %s", 2562 notComponent, completeFilter, entry.getName()); 2563 } 2564 return ConditionResult.FALSE; 2565 case FALSE: 2566 if (logger.isTraceEnabled()) 2567 { 2568 logger.trace( 2569 "Returning TRUE for NOT component %s in filter " + 2570 "%s for entry %s", 2571 notComponent, completeFilter, entry.getName()); 2572 } 2573 return ConditionResult.TRUE; 2574 case UNDEFINED: 2575 if (logger.isTraceEnabled()) 2576 { 2577 logger.trace( 2578 "Undefined result for NOT component %s in filter " + 2579 "%s for entry %s", 2580 notComponent, completeFilter, entry.getName()); 2581 } 2582 return ConditionResult.UNDEFINED; 2583 default: 2584 LocalizableMessage message = ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 2585 get(entry.getName(), completeFilter, result); 2586 throw new 2587 DirectoryException( 2588 DirectoryServer.getServerErrorResultCode(), 2589 message); 2590 } 2591 } 2592 } 2593 2594 2595 2596 /** 2597 * Indicates whether the this equality filter matches the provided 2598 * entry. 2599 * 2600 * @param completeFilter The complete filter being checked, of 2601 * which this filter may be a subset. 2602 * @param entry The entry for which to make the 2603 * determination. 2604 * 2605 * @return <CODE>TRUE</CODE> if this filter matches the provided 2606 * entry, <CODE>FALSE</CODE> if it does not, or 2607 * <CODE>UNDEFINED</CODE> if the result is undefined. 2608 * 2609 * @throws DirectoryException If a problem is encountered during 2610 * processing. 2611 */ 2612 private ConditionResult processEquality(SearchFilter completeFilter, 2613 Entry entry) 2614 throws DirectoryException 2615 { 2616 // Make sure that an attribute type has been defined. 2617 if (attributeType == null) 2618 { 2619 LocalizableMessage message = 2620 ERR_SEARCH_FILTER_EQUALITY_NO_ATTRIBUTE_TYPE. 2621 get(entry.getName(), toString()); 2622 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2623 } 2624 2625 // Make sure that an assertion value has been defined. 2626 if (assertionValue == null) 2627 { 2628 LocalizableMessage message = 2629 ERR_SEARCH_FILTER_EQUALITY_NO_ASSERTION_VALUE. 2630 get(entry.getName(), toString(), attributeType.getNameOrOID()); 2631 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2632 } 2633 2634 // See if the entry has an attribute with the requested type. 2635 List<Attribute> attrs = entry.getAttribute(attributeDescription); 2636 if (attrs.isEmpty()) 2637 { 2638 if (logger.isTraceEnabled()) 2639 { 2640 logger.trace( 2641 "Returning FALSE for equality component %s in " + 2642 "filter %s because entry %s didn't have attribute " + 2643 "type %s", 2644 this, completeFilter, entry.getName(), 2645 attributeType.getNameOrOID()); 2646 } 2647 return ConditionResult.FALSE; 2648 } 2649 2650 // Get the equality matching rule for the given attribute type 2651 MatchingRule matchingRule = attributeType.getEqualityMatchingRule(); 2652 if (matchingRule == null) 2653 { 2654 if (logger.isTraceEnabled()) 2655 { 2656 logger.trace( 2657 "Attribute type %s does not have an equality matching " + 2658 "rule -- returning undefined.", 2659 attributeType.getNameOrOID()); 2660 } 2661 return ConditionResult.UNDEFINED; 2662 } 2663 2664 // Iterate through all the attributes and see if we can find a match. 2665 ConditionResult result = ConditionResult.FALSE; 2666 for (Attribute a : attrs) 2667 { 2668 final ConditionResult cr = a.matchesEqualityAssertion(assertionValue); 2669 if (cr == ConditionResult.TRUE) 2670 { 2671 if (logger.isTraceEnabled()) 2672 { 2673 logger.trace( 2674 "Returning TRUE for equality component %s in filter %s " + 2675 "for entry %s", this, completeFilter, entry.getName()); 2676 } 2677 return ConditionResult.TRUE; 2678 } 2679 else if (cr == ConditionResult.UNDEFINED) 2680 { 2681 result = ConditionResult.UNDEFINED; 2682 } 2683 } 2684 2685 if (logger.isTraceEnabled()) 2686 { 2687 logger.trace( 2688 "Returning %s for equality component %s in filter %s " + 2689 "because entry %s didn't have attribute type %s with value %s", 2690 result, this, completeFilter, entry.getName(), attributeType.getNameOrOID(), assertionValue); 2691 } 2692 return result; 2693 } 2694 2695 2696 2697 /** 2698 * Indicates whether the this substring filter matches the provided 2699 * entry. 2700 * 2701 * @param completeFilter The complete filter being checked, of 2702 * which this filter may be a subset. 2703 * @param entry The entry for which to make the 2704 * determination. 2705 * 2706 * @return <CODE>TRUE</CODE> if this filter matches the provided 2707 * entry, <CODE>FALSE</CODE> if it does not, or 2708 * <CODE>UNDEFINED</CODE> if the result is undefined. 2709 * 2710 * @throws DirectoryException If a problem is encountered during 2711 * processing. 2712 */ 2713 private ConditionResult processSubstring( 2714 SearchFilter completeFilter, 2715 Entry entry) 2716 throws DirectoryException 2717 { 2718 // Make sure that an attribute type has been defined. 2719 if (attributeType == null) 2720 { 2721 LocalizableMessage message = 2722 ERR_SEARCH_FILTER_SUBSTRING_NO_ATTRIBUTE_TYPE. 2723 get(entry.getName(), toString()); 2724 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2725 } 2726 2727 // Make sure that at least one substring element has been defined. 2728 if (subInitialElement == null && 2729 subFinalElement == null && 2730 (subAnyElements == null || subAnyElements.isEmpty())) 2731 { 2732 LocalizableMessage message = 2733 ERR_SEARCH_FILTER_SUBSTRING_NO_SUBSTRING_COMPONENTS. 2734 get(entry.getName(), toString(), attributeType.getNameOrOID()); 2735 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2736 } 2737 2738 // See if the entry has an attribute with the requested type. 2739 List<Attribute> attrs = entry.getAttribute(attributeDescription); 2740 if (attrs.isEmpty()) 2741 { 2742 if (logger.isTraceEnabled()) 2743 { 2744 logger.trace( 2745 "Returning FALSE for substring component %s in " + 2746 "filter %s because entry %s didn't have attribute " + 2747 "type %s", 2748 this, completeFilter, entry.getName(), 2749 attributeType.getNameOrOID()); 2750 } 2751 return ConditionResult.FALSE; 2752 } 2753 2754 // Iterate through all the attributes and see if we can find a 2755 // match. 2756 ConditionResult result = ConditionResult.FALSE; 2757 for (Attribute a : attrs) 2758 { 2759 switch (a.matchesSubstring(subInitialElement, 2760 subAnyElements, 2761 subFinalElement)) 2762 { 2763 case TRUE: 2764 if (logger.isTraceEnabled()) 2765 { 2766 logger.trace( 2767 "Returning TRUE for substring component %s in " + 2768 "filter %s for entry %s", 2769 this, completeFilter, entry.getName()); 2770 } 2771 return ConditionResult.TRUE; 2772 case FALSE: 2773 break; 2774 case UNDEFINED: 2775 if (logger.isTraceEnabled()) 2776 { 2777 logger.trace( 2778 "Undefined result encountered for substring " + 2779 "component %s in filter %s for entry %s", 2780 this, completeFilter, entry.getName()); 2781 } 2782 result = ConditionResult.UNDEFINED; 2783 break; 2784 default: 2785 } 2786 } 2787 2788 if (logger.isTraceEnabled()) 2789 { 2790 logger.trace( 2791 "Returning %s for substring component %s in filter " + 2792 "%s for entry %s", 2793 result, this, completeFilter, entry.getName()); 2794 } 2795 return result; 2796 } 2797 2798 2799 2800 /** 2801 * Indicates whether the this greater-or-equal filter matches the 2802 * provided entry. 2803 * 2804 * @param completeFilter The complete filter being checked, of 2805 * which this filter may be a subset. 2806 * @param entry The entry for which to make the 2807 * determination. 2808 * 2809 * @return <CODE>TRUE</CODE> if this filter matches the provided 2810 * entry, <CODE>FALSE</CODE> if it does not, or 2811 * <CODE>UNDEFINED</CODE> if the result is undefined. 2812 * 2813 * @throws DirectoryException If a problem is encountered during 2814 * processing. 2815 */ 2816 private ConditionResult processGreaterOrEqual( 2817 SearchFilter completeFilter, 2818 Entry entry) 2819 throws DirectoryException 2820 { 2821 // Make sure that an attribute type has been defined. 2822 if (attributeType == null) 2823 { 2824 LocalizableMessage message = 2825 ERR_SEARCH_FILTER_GREATER_OR_EQUAL_NO_ATTRIBUTE_TYPE. 2826 get(entry.getName(), toString()); 2827 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2828 } 2829 2830 // Make sure that an assertion value has been defined. 2831 if (assertionValue == null) 2832 { 2833 LocalizableMessage message = 2834 ERR_SEARCH_FILTER_GREATER_OR_EQUAL_NO_VALUE. 2835 get(entry.getName(), toString(), attributeType.getNameOrOID()); 2836 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2837 } 2838 2839 // See if the entry has an attribute with the requested type. 2840 List<Attribute> attrs = entry.getAttribute(attributeDescription); 2841 if (attrs.isEmpty()) 2842 { 2843 if (logger.isTraceEnabled()) 2844 { 2845 logger.trace("Returning FALSE for " + 2846 "greater-or-equal component %s in filter %s " + 2847 "because entry %s didn't have attribute type %s", 2848 this, completeFilter, entry.getName(), 2849 attributeType.getNameOrOID()); 2850 } 2851 return ConditionResult.FALSE; 2852 } 2853 2854 // Iterate through all the attributes and see if we can find a match. 2855 ConditionResult result = ConditionResult.FALSE; 2856 for (Attribute a : attrs) 2857 { 2858 switch (a.greaterThanOrEqualTo(assertionValue)) 2859 { 2860 case TRUE: 2861 if (logger.isTraceEnabled()) 2862 { 2863 logger.trace( 2864 "Returning TRUE for greater-or-equal component " + 2865 "%s in filter %s for entry %s", 2866 this, completeFilter, entry.getName()); 2867 } 2868 return ConditionResult.TRUE; 2869 case FALSE: 2870 break; 2871 case UNDEFINED: 2872 if (logger.isTraceEnabled()) 2873 { 2874 logger.trace( 2875 "Undefined result encountered for " + 2876 "greater-or-equal component %s in filter %s " + 2877 "for entry %s", this, completeFilter, 2878 entry.getName()); 2879 } 2880 result = ConditionResult.UNDEFINED; 2881 break; 2882 default: 2883 } 2884 } 2885 2886 if (logger.isTraceEnabled()) 2887 { 2888 logger.trace( 2889 "Returning %s for greater-or-equal component %s in " + 2890 "filter %s for entry %s", 2891 result, this, completeFilter, entry.getName()); 2892 } 2893 return result; 2894 } 2895 2896 2897 2898 /** 2899 * Indicates whether the this less-or-equal filter matches the 2900 * provided entry. 2901 * 2902 * @param completeFilter The complete filter being checked, of 2903 * which this filter may be a subset. 2904 * @param entry The entry for which to make the 2905 * determination. 2906 * 2907 * @return <CODE>TRUE</CODE> if this filter matches the provided 2908 * entry, <CODE>FALSE</CODE> if it does not, or 2909 * <CODE>UNDEFINED</CODE> if the result is undefined. 2910 * 2911 * @throws DirectoryException If a problem is encountered during 2912 * processing. 2913 */ 2914 private ConditionResult processLessOrEqual( 2915 SearchFilter completeFilter, 2916 Entry entry) 2917 throws DirectoryException 2918 { 2919 // Make sure that an attribute type has been defined. 2920 if (attributeType == null) 2921 { 2922 LocalizableMessage message = 2923 ERR_SEARCH_FILTER_LESS_OR_EQUAL_NO_ATTRIBUTE_TYPE. 2924 get(entry.getName(), toString()); 2925 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2926 } 2927 2928 // Make sure that an assertion value has been defined. 2929 if (assertionValue == null) 2930 { 2931 LocalizableMessage message = 2932 ERR_SEARCH_FILTER_LESS_OR_EQUAL_NO_ASSERTION_VALUE. 2933 get(entry.getName(), toString(), attributeType.getNameOrOID()); 2934 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 2935 } 2936 2937 // See if the entry has an attribute with the requested type. 2938 List<Attribute> attrs = entry.getAttribute(attributeDescription); 2939 if (attrs.isEmpty()) 2940 { 2941 if (logger.isTraceEnabled()) 2942 { 2943 logger.trace( 2944 "Returning FALSE for less-or-equal component %s in " + 2945 "filter %s because entry %s didn't have attribute " + 2946 "type %s", this, completeFilter, entry.getName(), 2947 attributeType.getNameOrOID()); 2948 } 2949 return ConditionResult.FALSE; 2950 } 2951 2952 // Iterate through all the attributes and see if we can find a 2953 // match. 2954 ConditionResult result = ConditionResult.FALSE; 2955 for (Attribute a : attrs) 2956 { 2957 switch (a.lessThanOrEqualTo(assertionValue)) 2958 { 2959 case TRUE: 2960 if (logger.isTraceEnabled()) 2961 { 2962 logger.trace( 2963 "Returning TRUE for less-or-equal component %s " + 2964 "in filter %s for entry %s", 2965 this, completeFilter, entry.getName()); 2966 } 2967 return ConditionResult.TRUE; 2968 case FALSE: 2969 break; 2970 case UNDEFINED: 2971 if (logger.isTraceEnabled()) 2972 { 2973 logger.trace( 2974 "Undefined result encountered for " + 2975 "less-or-equal component %s in filter %s " + 2976 "for entry %s", 2977 this, completeFilter, entry.getName()); 2978 } 2979 result = ConditionResult.UNDEFINED; 2980 break; 2981 default: 2982 } 2983 } 2984 2985 if (logger.isTraceEnabled()) 2986 { 2987 logger.trace( 2988 "Returning %s for less-or-equal component %s in " + 2989 "filter %s for entry %s", 2990 result, this, completeFilter, entry.getName()); 2991 } 2992 return result; 2993 } 2994 2995 2996 2997 /** 2998 * Indicates whether the this present filter matches the provided 2999 * entry. 3000 * 3001 * @param completeFilter The complete filter being checked, of 3002 * which this filter may be a subset. 3003 * @param entry The entry for which to make the 3004 * determination. 3005 * 3006 * @return <CODE>TRUE</CODE> if this filter matches the provided 3007 * entry, <CODE>FALSE</CODE> if it does not, or 3008 * <CODE>UNDEFINED</CODE> if the result is undefined. 3009 * 3010 * @throws DirectoryException If a problem is encountered during 3011 * processing. 3012 */ 3013 private ConditionResult processPresent(SearchFilter completeFilter, 3014 Entry entry) 3015 throws DirectoryException 3016 { 3017 // Make sure that an attribute type has been defined. 3018 if (attributeType == null) 3019 { 3020 LocalizableMessage message = 3021 ERR_SEARCH_FILTER_PRESENCE_NO_ATTRIBUTE_TYPE. 3022 get(entry.getName(), toString()); 3023 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 3024 } 3025 3026 3027 // See if the entry has an attribute with the requested type. 3028 // If so, then it's a match. If not, then it's not a match. 3029 ConditionResult result = ConditionResult.valueOf(entry.hasAttribute(attributeDescription)); 3030 if (logger.isTraceEnabled()) 3031 { 3032 logger.trace( 3033 "Returning %s for presence component %s in filter %s for entry %s", 3034 result, this, completeFilter, entry.getName()); 3035 } 3036 return result; 3037 } 3038 3039 3040 3041 /** 3042 * Indicates whether the this approximate filter matches the 3043 * provided entry. 3044 * 3045 * @param completeFilter The complete filter being checked, of 3046 * which this filter may be a subset. 3047 * @param entry The entry for which to make the 3048 * determination. 3049 * 3050 * @return <CODE>TRUE</CODE> if this filter matches the provided 3051 * entry, <CODE>FALSE</CODE> if it does not, or 3052 * <CODE>UNDEFINED</CODE> if the result is undefined. 3053 * 3054 * @throws DirectoryException If a problem is encountered during 3055 * processing. 3056 */ 3057 private ConditionResult processApproximate( 3058 SearchFilter completeFilter, 3059 Entry entry) 3060 throws DirectoryException 3061 { 3062 // Make sure that an attribute type has been defined. 3063 if (attributeType == null) 3064 { 3065 LocalizableMessage message = 3066 ERR_SEARCH_FILTER_APPROXIMATE_NO_ATTRIBUTE_TYPE. 3067 get(entry.getName(), toString()); 3068 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 3069 } 3070 3071 // Make sure that an assertion value has been defined. 3072 if (assertionValue == null) 3073 { 3074 LocalizableMessage message = 3075 ERR_SEARCH_FILTER_APPROXIMATE_NO_ASSERTION_VALUE. 3076 get(entry.getName(), toString(), attributeType.getNameOrOID()); 3077 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 3078 } 3079 3080 // See if the entry has an attribute with the requested type. 3081 List<Attribute> attrs = entry.getAttribute(attributeDescription); 3082 if (attrs.isEmpty()) 3083 { 3084 if (logger.isTraceEnabled()) 3085 { 3086 logger.trace( 3087 "Returning FALSE for approximate component %s in " + 3088 "filter %s because entry %s didn't have attribute " + 3089 "type %s", this, completeFilter, entry.getName(), 3090 attributeType.getNameOrOID()); 3091 } 3092 return ConditionResult.FALSE; 3093 } 3094 3095 // Iterate through all the attributes and see if we can find a 3096 // match. 3097 ConditionResult result = ConditionResult.FALSE; 3098 for (Attribute a : attrs) 3099 { 3100 switch (a.approximatelyEqualTo(assertionValue)) 3101 { 3102 case TRUE: 3103 if (logger.isTraceEnabled()) 3104 { 3105 logger.trace( 3106 "Returning TRUE for approximate component %s in " + 3107 "filter %s for entry %s", 3108 this, completeFilter, entry.getName()); 3109 } 3110 return ConditionResult.TRUE; 3111 case FALSE: 3112 break; 3113 case UNDEFINED: 3114 if (logger.isTraceEnabled()) 3115 { 3116 logger.trace( 3117 "Undefined result encountered for approximate " + 3118 "component %s in filter %s for entry %s", 3119 this, completeFilter, entry.getName()); 3120 } 3121 result = ConditionResult.UNDEFINED; 3122 break; 3123 default: 3124 } 3125 } 3126 3127 if (logger.isTraceEnabled()) 3128 { 3129 logger.trace( 3130 "Returning %s for approximate component %s in filter " + 3131 "%s for entry %s", 3132 result, this, completeFilter, entry.getName()); 3133 } 3134 return result; 3135 } 3136 3137 3138 3139 /** 3140 * Indicates whether this extensibleMatch filter matches the 3141 * provided entry. 3142 * 3143 * @param completeFilter The complete filter in which this 3144 * extensibleMatch filter may be a 3145 * subcomponent. 3146 * @param entry The entry for which to make the 3147 * determination. 3148 * 3149 * @return <CODE>TRUE</CODE> if this extensibleMatch filter matches 3150 * the provided entry, <CODE>FALSE</CODE> if it does not, or 3151 * <CODE>UNDEFINED</CODE> if the result cannot be 3152 * determined. 3153 * 3154 * @throws DirectoryException If a problem occurs while evaluating 3155 * this filter against the provided 3156 * entry. 3157 */ 3158 private ConditionResult processExtensibleMatch( 3159 SearchFilter completeFilter, 3160 Entry entry) 3161 throws DirectoryException 3162 { 3163 // We must have an assertion value for which to make the 3164 // determination. 3165 if (assertionValue == null) 3166 { 3167 LocalizableMessage message = 3168 ERR_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_ASSERTION_VALUE. 3169 get(entry.getName(), completeFilter); 3170 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 3171 message); 3172 } 3173 3174 3175 MatchingRule matchingRule = null; 3176 3177 if (matchingRuleID != null) 3178 { 3179 matchingRule = 3180 DirectoryServer.getMatchingRule( 3181 toLowerCase(matchingRuleID)); 3182 if (matchingRule == null) 3183 { 3184 if (logger.isTraceEnabled()) 3185 { 3186 logger.trace( 3187 "Unknown matching rule %s defined in extensibleMatch " + 3188 "component of filter %s -- returning undefined.", 3189 matchingRuleID, this); 3190 } 3191 return ConditionResult.UNDEFINED; 3192 } 3193 } 3194 else 3195 { 3196 if (attributeType == null) 3197 { 3198 LocalizableMessage message = 3199 ERR_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_RULE_OR_TYPE. 3200 get(entry.getName(), completeFilter); 3201 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 3202 message); 3203 } 3204 else 3205 { 3206 matchingRule = attributeType.getEqualityMatchingRule(); 3207 if (matchingRule == null) 3208 { 3209 if (logger.isTraceEnabled()) 3210 { 3211 logger.trace( 3212 "Attribute type %s does not have an equality matching " + 3213 "rule -- returning undefined.", 3214 attributeType.getNameOrOID()); 3215 } 3216 return ConditionResult.UNDEFINED; 3217 } 3218 } 3219 } 3220 3221 3222 // If there is an attribute type, then check to see if there is a 3223 // corresponding matching rule use for the matching rule and 3224 // determine if it allows that attribute type. 3225 if (attributeType != null) 3226 { 3227 MatchingRuleUse mru = 3228 DirectoryServer.getMatchingRuleUse(matchingRule); 3229 if (mru != null && !mru.appliesToAttribute(attributeType)) 3230 { 3231 if (logger.isTraceEnabled()) 3232 { 3233 logger.trace( 3234 "Attribute type %s is not allowed for use with " + 3235 "matching rule %s because of matching rule use " + 3236 "definition %s", attributeType.getNameOrOID(), 3237 matchingRule.getNameOrOID(), mru.getNameOrOID()); 3238 } 3239 return ConditionResult.UNDEFINED; 3240 } 3241 } 3242 3243 3244 // Normalize the assertion value using the matching rule. 3245 Assertion assertion; 3246 try 3247 { 3248 assertion = matchingRule.getAssertion(assertionValue); 3249 } 3250 catch (Exception e) 3251 { 3252 logger.traceException(e); 3253 3254 // We can't normalize the assertion value, so the result must be 3255 // undefined. 3256 return ConditionResult.UNDEFINED; 3257 } 3258 3259 3260 // If there is an attribute type, then we should only check for 3261 // that attribute. Otherwise, we should check against all 3262 // attributes in the entry. 3263 ConditionResult result = ConditionResult.FALSE; 3264 if (attributeType == null) 3265 { 3266 for (List<Attribute> attrList : 3267 entry.getUserAttributes().values()) 3268 { 3269 for (Attribute a : attrList) 3270 { 3271 for (ByteString v : a) 3272 { 3273 try 3274 { 3275 ByteString nv = matchingRule.normalizeAttributeValue(v); 3276 ConditionResult r = assertion.matches(nv); 3277 switch (r) 3278 { 3279 case TRUE: 3280 return ConditionResult.TRUE; 3281 case FALSE: 3282 break; 3283 case UNDEFINED: 3284 result = ConditionResult.UNDEFINED; 3285 break; 3286 default: 3287 LocalizableMessage message = 3288 ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 3289 get(entry.getName(), completeFilter, r); 3290 throw new DirectoryException( 3291 ResultCode.PROTOCOL_ERROR, message); 3292 } 3293 } 3294 catch (Exception e) 3295 { 3296 logger.traceException(e); 3297 3298 // We couldn't normalize one of the values. If we don't 3299 // find a definite match, then we should return 3300 // undefined. 3301 result = ConditionResult.UNDEFINED; 3302 } 3303 } 3304 } 3305 } 3306 3307 for (List<Attribute> attrList : 3308 entry.getOperationalAttributes().values()) 3309 { 3310 for (Attribute a : attrList) 3311 { 3312 for (ByteString v : a) 3313 { 3314 try 3315 { 3316 ByteString nv = matchingRule.normalizeAttributeValue(v); 3317 ConditionResult r = assertion.matches(nv); 3318 switch (r) 3319 { 3320 case TRUE: 3321 return ConditionResult.TRUE; 3322 case FALSE: 3323 break; 3324 case UNDEFINED: 3325 result = ConditionResult.UNDEFINED; 3326 break; 3327 default: 3328 LocalizableMessage message = 3329 ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 3330 get(entry.getName(), completeFilter, r); 3331 throw new DirectoryException( 3332 ResultCode.PROTOCOL_ERROR, message); 3333 } 3334 } 3335 catch (Exception e) 3336 { 3337 logger.traceException(e); 3338 3339 // We couldn't normalize one of the values. If we don't 3340 // find a definite match, then we should return 3341 // undefined. 3342 result = ConditionResult.UNDEFINED; 3343 } 3344 } 3345 } 3346 } 3347 3348 Attribute a = entry.getObjectClassAttribute(); 3349 for (ByteString v : a) 3350 { 3351 try 3352 { 3353 ByteString nv = matchingRule.normalizeAttributeValue(v); 3354 ConditionResult r = assertion.matches(nv); 3355 switch (r) 3356 { 3357 case TRUE: 3358 return ConditionResult.TRUE; 3359 case FALSE: 3360 break; 3361 case UNDEFINED: 3362 result = ConditionResult.UNDEFINED; 3363 break; 3364 default: 3365 LocalizableMessage message = ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 3366 get(entry.getName(), completeFilter, r); 3367 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 3368 message); 3369 } 3370 } 3371 catch (Exception e) 3372 { 3373 logger.traceException(e); 3374 3375 // We couldn't normalize one of the values. If we don't 3376 // find a definite match, then we should return undefined. 3377 result = ConditionResult.UNDEFINED; 3378 } 3379 } 3380 } 3381 else 3382 { 3383 for (Attribute a : entry.getAttribute(attributeDescription)) 3384 { 3385 for (ByteString v : a) 3386 { 3387 try 3388 { 3389 ByteString nv = matchingRule.normalizeAttributeValue(v); 3390 ConditionResult r = assertion.matches(nv); 3391 switch (r) 3392 { 3393 case TRUE: 3394 return ConditionResult.TRUE; 3395 case FALSE: 3396 break; 3397 case UNDEFINED: 3398 result = ConditionResult.UNDEFINED; 3399 break; 3400 default: 3401 LocalizableMessage message = 3402 ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.get(entry.getName(), completeFilter, r); 3403 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 3404 } 3405 } 3406 catch (Exception e) 3407 { 3408 logger.traceException(e); 3409 3410 // We couldn't normalize one of the values. 3411 // If we don't find a definite match, then we should return undefined. 3412 result = ConditionResult.UNDEFINED; 3413 } 3414 } 3415 } 3416 } 3417 3418 3419 // If we've gotten here, then we know that there is no definite 3420 // match in the set of attributes. If we should check DN 3421 // attributes, then do so. 3422 if (dnAttributes) 3423 { 3424 for (RDN rdn : entry.getName()) 3425 { 3426 for (AVA ava : rdn) 3427 { 3428 try 3429 { 3430 if (attributeType == null || attributeType.equals(ava.getAttributeType())) 3431 { 3432 ByteString v = ava.getAttributeValue(); 3433 ByteString nv = matchingRule.normalizeAttributeValue(v); 3434 ConditionResult r = assertion.matches(nv); 3435 switch (r) 3436 { 3437 case TRUE: 3438 return ConditionResult.TRUE; 3439 case FALSE: 3440 break; 3441 case UNDEFINED: 3442 result = ConditionResult.UNDEFINED; 3443 break; 3444 default: 3445 LocalizableMessage message = 3446 ERR_SEARCH_FILTER_INVALID_RESULT_TYPE. 3447 get(entry.getName(), completeFilter, r); 3448 throw new DirectoryException( 3449 ResultCode.PROTOCOL_ERROR, message); 3450 } 3451 } 3452 } 3453 catch (Exception e) 3454 { 3455 logger.traceException(e); 3456 3457 // We couldn't normalize one of the values. If we don't 3458 // find a definite match, then we should return undefined. 3459 result = ConditionResult.UNDEFINED; 3460 } 3461 } 3462 } 3463 } 3464 3465 3466 // If we've gotten here, then there is no definitive match, so 3467 // we'll either return FALSE or UNDEFINED. 3468 return result; 3469 } 3470 3471 3472 /** 3473 * Indicates whether this search filter is equal to the provided 3474 * object. 3475 * 3476 * @param o The object for which to make the determination. 3477 * 3478 * @return <CODE>true</CODE> if the provide object is equal to this 3479 * search filter, or <CODE>false</CODE> if it is not. 3480 */ 3481 @Override 3482 public boolean equals(Object o) 3483 { 3484 if (o == null) 3485 { 3486 return false; 3487 } 3488 3489 if (o == this) 3490 { 3491 return true; 3492 } 3493 3494 if (! (o instanceof SearchFilter)) 3495 { 3496 return false; 3497 } 3498 3499 3500 SearchFilter f = (SearchFilter) o; 3501 if (filterType != f.filterType) 3502 { 3503 return false; 3504 } 3505 3506 3507 switch (filterType) 3508 { 3509 case AND: 3510 case OR: 3511 return andOrEqual(f); 3512 case NOT: 3513 return notComponent.equals(f.notComponent); 3514 case SUBSTRING: 3515 return substringEqual(f); 3516 case PRESENT: 3517 return attributeDescription.equals(f.attributeDescription); 3518 case EXTENSIBLE_MATCH: 3519 return extensibleEqual(f); 3520 case EQUALITY: 3521 case APPROXIMATE_MATCH: 3522 case GREATER_OR_EQUAL: 3523 case LESS_OR_EQUAL: 3524 return attributeDescription.equals(f.attributeDescription) 3525 && assertionValue.equals(f.assertionValue); 3526 default: 3527 return false; 3528 } 3529 } 3530 3531 private boolean andOrEqual(SearchFilter f) 3532 { 3533 if (filterComponents.size() != f.filterComponents.size()) 3534 { 3535 return false; 3536 } 3537 3538 for (SearchFilter outerFilter : filterComponents) 3539 { 3540 if (!f.filterComponents.contains(outerFilter)) 3541 { 3542 return false; 3543 } 3544 } 3545 return true; 3546 } 3547 3548 private boolean substringEqual(SearchFilter other) 3549 { 3550 if (!attributeDescription.equals(other.attributeDescription)) 3551 { 3552 return false; 3553 } 3554 3555 MatchingRule rule = attributeType.getSubstringMatchingRule(); 3556 if (rule == null) 3557 { 3558 return false; 3559 } 3560 3561 boolean initialCheck = subInitialElement == null ? 3562 other.subInitialElement == null : subInitialElement.equals(other.subInitialElement); 3563 if (!initialCheck) 3564 { 3565 return false; 3566 } 3567 boolean finalCheck = subFinalElement == null ? 3568 other.subFinalElement == null : subFinalElement.equals(other.subFinalElement); 3569 if (!finalCheck) 3570 { 3571 return false; 3572 } 3573 boolean anyCheck = subAnyElements == null ? 3574 other.subAnyElements == null : subAnyElements.size() == other.subAnyElements.size(); 3575 if (!anyCheck) 3576 { 3577 return false; 3578 } 3579 if (subAnyElements != null) 3580 { 3581 for (int i = 0; i < subAnyElements.size(); i++) 3582 { 3583 if (! subAnyElements.get(i).equals(other.subAnyElements.get(i))) 3584 { 3585 return false; 3586 } 3587 } 3588 } 3589 return true; 3590 } 3591 3592 private boolean extensibleEqual(SearchFilter f) 3593 { 3594 if (attributeType == null) 3595 { 3596 if (f.attributeType != null) 3597 { 3598 return false; 3599 } 3600 } 3601 else if (!attributeDescription.equals(f.attributeDescription)) 3602 { 3603 return false; 3604 } 3605 3606 if (dnAttributes != f.dnAttributes) 3607 { 3608 return false; 3609 } 3610 3611 if (matchingRuleID == null) 3612 { 3613 if (f.matchingRuleID != null) 3614 { 3615 return false; 3616 } 3617 } 3618 else 3619 { 3620 if (! matchingRuleID.equals(f.matchingRuleID)) 3621 { 3622 return false; 3623 } 3624 } 3625 3626 if (assertionValue == null) 3627 { 3628 if (f.assertionValue != null) 3629 { 3630 return false; 3631 } 3632 } 3633 else 3634 { 3635 if (matchingRuleID == null) 3636 { 3637 if (! assertionValue.equals(f.assertionValue)) 3638 { 3639 return false; 3640 } 3641 } 3642 else 3643 { 3644 MatchingRule mrule = DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID)); 3645 if (mrule == null) 3646 { 3647 return false; 3648 } 3649 else 3650 { 3651 try 3652 { 3653 Assertion assertion = mrule.getAssertion(f.assertionValue); 3654 return assertion.matches(mrule.normalizeAttributeValue(assertionValue)).toBoolean(); 3655 } 3656 catch (Exception e) 3657 { 3658 return false; 3659 } 3660 } 3661 } 3662 } 3663 3664 return true; 3665 } 3666 3667 /** 3668 * Retrieves the hash code for this search filter. 3669 * 3670 * @return The hash code for this search filter. 3671 */ 3672 @Override 3673 public int hashCode() 3674 { 3675 switch (filterType) 3676 { 3677 case AND: 3678 case OR: 3679 int hashCode = 0; 3680 3681 for (SearchFilter filterComp : filterComponents) 3682 { 3683 hashCode += filterComp.hashCode(); 3684 } 3685 3686 return hashCode; 3687 case NOT: 3688 return notComponent.hashCode(); 3689 case EQUALITY: 3690 return typeAndAssertionHashCode(); 3691 case SUBSTRING: 3692 return substringHashCode(); 3693 case GREATER_OR_EQUAL: 3694 return typeAndAssertionHashCode(); 3695 case LESS_OR_EQUAL: 3696 return typeAndAssertionHashCode(); 3697 case PRESENT: 3698 return attributeType.hashCode(); 3699 case APPROXIMATE_MATCH: 3700 return typeAndAssertionHashCode(); 3701 case EXTENSIBLE_MATCH: 3702 return extensibleHashCode(); 3703 default: 3704 return 1; 3705 } 3706 } 3707 3708 3709 /** Returns the hash code for extensible filter. */ 3710 private int extensibleHashCode() 3711 { 3712 int hashCode = 0; 3713 3714 if (attributeType != null) 3715 { 3716 hashCode += attributeType.hashCode(); 3717 } 3718 3719 if (dnAttributes) 3720 { 3721 hashCode++; 3722 } 3723 3724 if (matchingRuleID != null) 3725 { 3726 hashCode += matchingRuleID.hashCode(); 3727 } 3728 3729 if (assertionValue != null) 3730 { 3731 hashCode += assertionValue.hashCode(); 3732 } 3733 return hashCode; 3734 } 3735 3736 3737 private int typeAndAssertionHashCode() 3738 { 3739 return attributeType.hashCode() + assertionValue.hashCode(); 3740 } 3741 3742 /** Returns hash code to use for substring filter. */ 3743 private int substringHashCode() 3744 { 3745 int hashCode = attributeType.hashCode(); 3746 if (subInitialElement != null) 3747 { 3748 hashCode += subInitialElement.hashCode(); 3749 } 3750 if (subAnyElements != null) 3751 { 3752 for (ByteString e : subAnyElements) 3753 { 3754 hashCode += e.hashCode(); 3755 } 3756 } 3757 if (subFinalElement != null) 3758 { 3759 hashCode += subFinalElement.hashCode(); 3760 } 3761 return hashCode; 3762 } 3763 3764 3765 3766 /** 3767 * Retrieves a string representation of this search filter. 3768 * 3769 * @return A string representation of this search filter. 3770 */ 3771 @Override 3772 public String toString() 3773 { 3774 StringBuilder buffer = new StringBuilder(); 3775 toString(buffer); 3776 return buffer.toString(); 3777 } 3778 3779 3780 3781 /** 3782 * Appends a string representation of this search filter to the 3783 * provided buffer. 3784 * 3785 * @param buffer The buffer to which the information should be 3786 * appended. 3787 */ 3788 public void toString(StringBuilder buffer) 3789 { 3790 switch (filterType) 3791 { 3792 case AND: 3793 buffer.append("(&"); 3794 for (SearchFilter f : filterComponents) 3795 { 3796 f.toString(buffer); 3797 } 3798 buffer.append(")"); 3799 break; 3800 case OR: 3801 buffer.append("(|"); 3802 for (SearchFilter f : filterComponents) 3803 { 3804 f.toString(buffer); 3805 } 3806 buffer.append(")"); 3807 break; 3808 case NOT: 3809 buffer.append("(!"); 3810 notComponent.toString(buffer); 3811 buffer.append(")"); 3812 break; 3813 case EQUALITY: 3814 appendEquation(buffer, "="); 3815 break; 3816 case SUBSTRING: 3817 buffer.append("("); 3818 buffer.append(attributeDescription); 3819 buffer.append("="); 3820 3821 if (subInitialElement != null) 3822 { 3823 valueToFilterString(buffer, subInitialElement); 3824 } 3825 3826 if (subAnyElements != null && !subAnyElements.isEmpty()) 3827 { 3828 for (ByteString s : subAnyElements) 3829 { 3830 buffer.append("*"); 3831 valueToFilterString(buffer, s); 3832 } 3833 } 3834 3835 buffer.append("*"); 3836 3837 if (subFinalElement != null) 3838 { 3839 valueToFilterString(buffer, subFinalElement); 3840 } 3841 3842 buffer.append(")"); 3843 break; 3844 case GREATER_OR_EQUAL: 3845 appendEquation(buffer, ">="); 3846 break; 3847 case LESS_OR_EQUAL: 3848 appendEquation(buffer, "<="); 3849 break; 3850 case PRESENT: 3851 buffer.append("("); 3852 buffer.append(attributeDescription); 3853 buffer.append("=*)"); 3854 break; 3855 case APPROXIMATE_MATCH: 3856 appendEquation(buffer, "~="); 3857 break; 3858 case EXTENSIBLE_MATCH: 3859 buffer.append("("); 3860 3861 if (attributeDescription != null) 3862 { 3863 buffer.append(attributeDescription); 3864 } 3865 3866 if (dnAttributes) 3867 { 3868 buffer.append(":dn"); 3869 } 3870 3871 if (matchingRuleID != null) 3872 { 3873 buffer.append(":"); 3874 buffer.append(matchingRuleID); 3875 } 3876 3877 buffer.append(":="); 3878 valueToFilterString(buffer, assertionValue); 3879 buffer.append(")"); 3880 break; 3881 } 3882 } 3883 3884 private void appendEquation(StringBuilder buffer, String operator) 3885 { 3886 buffer.append("("); 3887 buffer.append(attributeDescription); 3888 buffer.append(operator); 3889 valueToFilterString(buffer, assertionValue); 3890 buffer.append(")"); 3891 } 3892 3893 3894 /** 3895 * Appends a properly-cleaned version of the provided value to the 3896 * given buffer so that it can be safely used in string 3897 * representations of this search filter. The formatting changes 3898 * that may be performed will be in compliance with the 3899 * specification in RFC 2254. 3900 * 3901 * @param buffer The buffer to which the "safe" version of the 3902 * value will be appended. 3903 * @param value The value to be appended to the buffer. 3904 */ 3905 private void valueToFilterString(StringBuilder buffer, 3906 ByteString value) 3907 { 3908 if (value == null) 3909 { 3910 return; 3911 } 3912 3913 3914 // Get the binary representation of the value and iterate through 3915 // it to see if there are any unsafe characters. If there are, 3916 // then escape them and replace them with a two-digit hex 3917 // equivalent. 3918 buffer.ensureCapacity(buffer.length() + value.length()); 3919 byte b; 3920 for (int i = 0; i < value.length(); i++) 3921 { 3922 b = value.byteAt(i); 3923 if (((b & 0x7F) != b) || // Not 7-bit clean 3924 (b <= 0x1F) || // Below the printable character range 3925 (b == 0x28) || // Open parenthesis 3926 (b == 0x29) || // Close parenthesis 3927 (b == 0x2A) || // Asterisk 3928 (b == 0x5C) || // Backslash 3929 (b == 0x7F)) // Delete character 3930 { 3931 buffer.append("\\"); 3932 buffer.append(byteToHex(b)); 3933 } 3934 else 3935 { 3936 buffer.append((char) b); 3937 } 3938 } 3939 } 3940 3941 /** 3942 * Returns the {@code objectClass} presence filter {@code (objectClass=*)}. 3943 * 3944 * @return The {@code objectClass} presence filter {@code (objectClass=*)}. 3945 */ 3946 public static SearchFilter objectClassPresent() 3947 { 3948 if (objectClassPresent == null) 3949 { 3950 try 3951 { 3952 objectClassPresent = SearchFilter.createFilterFromString("(objectclass=*)"); 3953 } 3954 catch (DirectoryException canNeverHappen) 3955 { 3956 logger.traceException(canNeverHappen); 3957 } 3958 } 3959 return objectClassPresent; 3960 } 3961}