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