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