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