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