001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 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.Debug; 051import com.unboundid.util.NotMutable; 052import com.unboundid.util.StaticUtils; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.Validator; 056 057import static com.unboundid.ldap.sdk.LDAPMessages.*; 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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.ensureNotNull(attributeName); 634 Validator.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 Validator.ensureNotNull(attributeName); 699 Validator.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 Validator.ensureNotNull(attributeName); 764 Validator.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 substring search filter with only a subInitial (starts with) 786 * component. 787 * 788 * @param attributeName The attribute name for this substring filter. It 789 * must not be {@code null}. 790 * @param subInitial The subInitial component for this substring filter. 791 * It must not be {@code null}. 792 * 793 * @return The created substring search filter. 794 */ 795 public static Filter createSubInitialFilter(final String attributeName, 796 final String subInitial) 797 { 798 return createSubstringFilter(attributeName, subInitial, null, null); 799 } 800 801 802 803 /** 804 * Creates a new substring search filter with only a subInitial (starts with) 805 * component. 806 * 807 * @param attributeName The attribute name for this substring filter. It 808 * must not be {@code null}. 809 * @param subInitial The subInitial component for this substring filter. 810 * It must not be {@code null}. 811 * 812 * @return The created substring search filter. 813 */ 814 public static Filter createSubInitialFilter(final String attributeName, 815 final byte[] subInitial) 816 { 817 return createSubstringFilter(attributeName, subInitial, null, null); 818 } 819 820 821 822 /** 823 * Creates a new substring search filter with only a subAny (contains) 824 * component. 825 * 826 * @param attributeName The attribute name for this substring filter. It 827 * must not be {@code null}. 828 * @param subAny The subAny values for this substring filter. It 829 * must not be {@code null} or empty. 830 * 831 * @return The created substring search filter. 832 */ 833 public static Filter createSubAnyFilter(final String attributeName, 834 final String... subAny) 835 { 836 return createSubstringFilter(attributeName, null, subAny, null); 837 } 838 839 840 841 /** 842 * Creates a new substring search filter with only a subAny (contains) 843 * component. 844 * 845 * @param attributeName The attribute name for this substring filter. It 846 * must not be {@code null}. 847 * @param subAny The subAny values for this substring filter. It 848 * must not be {@code null} or empty. 849 * 850 * @return The created substring search filter. 851 */ 852 public static Filter createSubAnyFilter(final String attributeName, 853 final byte[]... subAny) 854 { 855 return createSubstringFilter(attributeName, null, subAny, null); 856 } 857 858 859 860 /** 861 * Creates a new substring search filter with only a subFinal (ends with) 862 * component. 863 * 864 * @param attributeName The attribute name for this substring filter. It 865 * must not be {@code null}. 866 * @param subFinal The subFinal component for this substring filter. 867 * It must not be {@code null}. 868 * 869 * @return The created substring search filter. 870 */ 871 public static Filter createSubFinalFilter(final String attributeName, 872 final String subFinal) 873 { 874 return createSubstringFilter(attributeName, null, null, subFinal); 875 } 876 877 878 879 /** 880 * Creates a new substring search filter with only a subFinal (ends with) 881 * component. 882 * 883 * @param attributeName The attribute name for this substring filter. It 884 * must not be {@code null}. 885 * @param subFinal The subFinal component for this substring filter. 886 * It must not be {@code null}. 887 * 888 * @return The created substring search filter. 889 */ 890 public static Filter createSubFinalFilter(final String attributeName, 891 final byte[] subFinal) 892 { 893 return createSubstringFilter(attributeName, null, null, subFinal); 894 } 895 896 897 898 /** 899 * Creates a new greater-or-equal search filter with the provided information. 900 * 901 * @param attributeName The attribute name for this greater-or-equal 902 * filter. It must not be {@code null}. 903 * @param assertionValue The assertion value for this greater-or-equal 904 * filter. It must not be {@code null}. 905 * 906 * @return The created greater-or-equal search filter. 907 */ 908 public static Filter createGreaterOrEqualFilter(final String attributeName, 909 final String assertionValue) 910 { 911 Validator.ensureNotNull(attributeName, assertionValue); 912 913 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 914 attributeName, new ASN1OctetString(assertionValue), null, 915 NO_SUB_ANY, null, null, false); 916 } 917 918 919 920 /** 921 * Creates a new greater-or-equal search filter with the provided information. 922 * 923 * @param attributeName The attribute name for this greater-or-equal 924 * filter. It must not be {@code null}. 925 * @param assertionValue The assertion value for this greater-or-equal 926 * filter. It must not be {@code null}. 927 * 928 * @return The created greater-or-equal search filter. 929 */ 930 public static Filter createGreaterOrEqualFilter(final String attributeName, 931 final byte[] assertionValue) 932 { 933 Validator.ensureNotNull(attributeName, assertionValue); 934 935 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 936 attributeName, new ASN1OctetString(assertionValue), null, 937 NO_SUB_ANY, null, null, false); 938 } 939 940 941 942 /** 943 * Creates a new greater-or-equal search filter with the provided information. 944 * 945 * @param attributeName The attribute name for this greater-or-equal 946 * filter. It must not be {@code null}. 947 * @param assertionValue The assertion value for this greater-or-equal 948 * filter. It must not be {@code null}. 949 * 950 * @return The created greater-or-equal search filter. 951 */ 952 static Filter createGreaterOrEqualFilter(final String attributeName, 953 final ASN1OctetString assertionValue) 954 { 955 Validator.ensureNotNull(attributeName, assertionValue); 956 957 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 958 attributeName, assertionValue, null, NO_SUB_ANY, null, 959 null, false); 960 } 961 962 963 964 /** 965 * Creates a new less-or-equal search filter with the provided information. 966 * 967 * @param attributeName The attribute name for this less-or-equal 968 * filter. It must not be {@code null}. 969 * @param assertionValue The assertion value for this less-or-equal 970 * filter. It must not be {@code null}. 971 * 972 * @return The created less-or-equal search filter. 973 */ 974 public static Filter createLessOrEqualFilter(final String attributeName, 975 final String assertionValue) 976 { 977 Validator.ensureNotNull(attributeName, assertionValue); 978 979 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 980 attributeName, new ASN1OctetString(assertionValue), null, 981 NO_SUB_ANY, null, null, false); 982 } 983 984 985 986 /** 987 * Creates a new less-or-equal search filter with the provided information. 988 * 989 * @param attributeName The attribute name for this less-or-equal 990 * filter. It must not be {@code null}. 991 * @param assertionValue The assertion value for this less-or-equal 992 * filter. It must not be {@code null}. 993 * 994 * @return The created less-or-equal search filter. 995 */ 996 public static Filter createLessOrEqualFilter(final String attributeName, 997 final byte[] assertionValue) 998 { 999 Validator.ensureNotNull(attributeName, assertionValue); 1000 1001 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 1002 attributeName, new ASN1OctetString(assertionValue), null, 1003 NO_SUB_ANY, null, null, false); 1004 } 1005 1006 1007 1008 /** 1009 * Creates a new less-or-equal search filter with the provided information. 1010 * 1011 * @param attributeName The attribute name for this less-or-equal 1012 * filter. It must not be {@code null}. 1013 * @param assertionValue The assertion value for this less-or-equal 1014 * filter. It must not be {@code null}. 1015 * 1016 * @return The created less-or-equal search filter. 1017 */ 1018 static Filter createLessOrEqualFilter(final String attributeName, 1019 final ASN1OctetString assertionValue) 1020 { 1021 Validator.ensureNotNull(attributeName, assertionValue); 1022 1023 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 1024 attributeName, assertionValue, null, NO_SUB_ANY, null, 1025 null, false); 1026 } 1027 1028 1029 1030 /** 1031 * Creates a new presence search filter with the provided information. 1032 * 1033 * @param attributeName The attribute name for this presence filter. It 1034 * must not be {@code null}. 1035 * 1036 * @return The created presence search filter. 1037 */ 1038 public static Filter createPresenceFilter(final String attributeName) 1039 { 1040 Validator.ensureNotNull(attributeName); 1041 1042 return new Filter(null, FILTER_TYPE_PRESENCE, NO_FILTERS, null, 1043 attributeName, null, null, NO_SUB_ANY, null, null, false); 1044 } 1045 1046 1047 1048 /** 1049 * Creates a new approximate match search filter with the provided 1050 * information. 1051 * 1052 * @param attributeName The attribute name for this approximate match 1053 * filter. It must not be {@code null}. 1054 * @param assertionValue The assertion value for this approximate match 1055 * filter. It must not be {@code null}. 1056 * 1057 * @return The created approximate match search filter. 1058 */ 1059 public static Filter createApproximateMatchFilter(final String attributeName, 1060 final String assertionValue) 1061 { 1062 Validator.ensureNotNull(attributeName, assertionValue); 1063 1064 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 1065 attributeName, new ASN1OctetString(assertionValue), null, 1066 NO_SUB_ANY, null, null, false); 1067 } 1068 1069 1070 1071 /** 1072 * Creates a new approximate match search filter with the provided 1073 * information. 1074 * 1075 * @param attributeName The attribute name for this approximate match 1076 * filter. It must not be {@code null}. 1077 * @param assertionValue The assertion value for this approximate match 1078 * filter. It must not be {@code null}. 1079 * 1080 * @return The created approximate match search filter. 1081 */ 1082 public static Filter createApproximateMatchFilter(final String attributeName, 1083 final byte[] assertionValue) 1084 { 1085 Validator.ensureNotNull(attributeName, assertionValue); 1086 1087 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 1088 attributeName, new ASN1OctetString(assertionValue), null, 1089 NO_SUB_ANY, null, null, false); 1090 } 1091 1092 1093 1094 /** 1095 * Creates a new approximate match search filter with the provided 1096 * information. 1097 * 1098 * @param attributeName The attribute name for this approximate match 1099 * filter. It must not be {@code null}. 1100 * @param assertionValue The assertion value for this approximate match 1101 * filter. It must not be {@code null}. 1102 * 1103 * @return The created approximate match search filter. 1104 */ 1105 static Filter createApproximateMatchFilter(final String attributeName, 1106 final ASN1OctetString assertionValue) 1107 { 1108 Validator.ensureNotNull(attributeName, assertionValue); 1109 1110 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 1111 attributeName, assertionValue, null, NO_SUB_ANY, null, 1112 null, false); 1113 } 1114 1115 1116 1117 /** 1118 * Creates a new extensible match search filter with the provided 1119 * information. At least one of the attribute name and matching rule ID must 1120 * be specified, and the assertion value must always be present. 1121 * 1122 * @param attributeName The attribute name for this extensible match 1123 * filter. 1124 * @param matchingRuleID The matching rule ID for this extensible match 1125 * filter. 1126 * @param dnAttributes Indicates whether the match should be performed 1127 * against attributes in the target entry's DN. 1128 * @param assertionValue The assertion value for this extensible match 1129 * filter. It must not be {@code null}. 1130 * 1131 * @return The created extensible match search filter. 1132 */ 1133 public static Filter createExtensibleMatchFilter(final String attributeName, 1134 final String matchingRuleID, 1135 final boolean dnAttributes, 1136 final String assertionValue) 1137 { 1138 Validator.ensureNotNull(assertionValue); 1139 Validator.ensureFalse((attributeName == null) && (matchingRuleID == null)); 1140 1141 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1142 attributeName, new ASN1OctetString(assertionValue), null, 1143 NO_SUB_ANY, null, matchingRuleID, dnAttributes); 1144 } 1145 1146 1147 1148 /** 1149 * Creates a new extensible match search filter with the provided 1150 * information. At least one of the attribute name and matching rule ID must 1151 * be specified, and the assertion value must always be present. 1152 * 1153 * @param attributeName The attribute name for this extensible match 1154 * filter. 1155 * @param matchingRuleID The matching rule ID for this extensible match 1156 * filter. 1157 * @param dnAttributes Indicates whether the match should be performed 1158 * against attributes in the target entry's DN. 1159 * @param assertionValue The assertion value for this extensible match 1160 * filter. It must not be {@code null}. 1161 * 1162 * @return The created extensible match search filter. 1163 */ 1164 public static Filter createExtensibleMatchFilter(final String attributeName, 1165 final String matchingRuleID, 1166 final boolean dnAttributes, 1167 final byte[] assertionValue) 1168 { 1169 Validator.ensureNotNull(assertionValue); 1170 Validator.ensureFalse((attributeName == null) && (matchingRuleID == null)); 1171 1172 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1173 attributeName, new ASN1OctetString(assertionValue), null, 1174 NO_SUB_ANY, null, matchingRuleID, dnAttributes); 1175 } 1176 1177 1178 1179 /** 1180 * Creates a new extensible match search filter with the provided 1181 * information. At least one of the attribute name and matching rule ID must 1182 * be specified, and the assertion value must always be present. 1183 * 1184 * @param attributeName The attribute name for this extensible match 1185 * filter. 1186 * @param matchingRuleID The matching rule ID for this extensible match 1187 * filter. 1188 * @param dnAttributes Indicates whether the match should be performed 1189 * against attributes in the target entry's DN. 1190 * @param assertionValue The assertion value for this extensible match 1191 * filter. It must not be {@code null}. 1192 * 1193 * @return The created approximate match search filter. 1194 */ 1195 static Filter createExtensibleMatchFilter(final String attributeName, 1196 final String matchingRuleID, final boolean dnAttributes, 1197 final ASN1OctetString assertionValue) 1198 { 1199 Validator.ensureNotNull(assertionValue); 1200 Validator.ensureFalse((attributeName == null) && (matchingRuleID == null)); 1201 1202 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1203 attributeName, assertionValue, null, NO_SUB_ANY, null, 1204 matchingRuleID, dnAttributes); 1205 } 1206 1207 1208 1209 /** 1210 * Creates a new search filter from the provided string representation. 1211 * 1212 * @param filterString The string representation of the filter to create. 1213 * It must not be {@code null}. 1214 * 1215 * @return The search filter decoded from the provided filter string. 1216 * 1217 * @throws LDAPException If the provided string cannot be decoded as a valid 1218 * LDAP search filter. 1219 */ 1220 public static Filter create(final String filterString) 1221 throws LDAPException 1222 { 1223 Validator.ensureNotNull(filterString); 1224 1225 return create(filterString, 0, (filterString.length() - 1), 0); 1226 } 1227 1228 1229 1230 /** 1231 * Creates a new search filter from the specified portion of the provided 1232 * string representation. 1233 * 1234 * @param filterString The string representation of the filter to create. 1235 * @param startPos The position of the first character to consider as 1236 * part of the filter. 1237 * @param endPos The position of the last character to consider as 1238 * part of the filter. 1239 * @param depth The current nesting depth for this filter. It should 1240 * be increased by one for each AND, OR, or NOT filter 1241 * encountered, in order to prevent stack overflow 1242 * errors from excessive recursion. 1243 * 1244 * @return The decoded search filter. 1245 * 1246 * @throws LDAPException If the provided string cannot be decoded as a valid 1247 * LDAP search filter. 1248 */ 1249 private static Filter create(final String filterString, final int startPos, 1250 final int endPos, final int depth) 1251 throws LDAPException 1252 { 1253 if (depth > 100) 1254 { 1255 throw new LDAPException(ResultCode.FILTER_ERROR, 1256 ERR_FILTER_TOO_DEEP.get(filterString)); 1257 } 1258 1259 final byte filterType; 1260 final Filter[] filterComps; 1261 final Filter notComp; 1262 final String attrName; 1263 final ASN1OctetString assertionValue; 1264 final ASN1OctetString subInitial; 1265 final ASN1OctetString[] subAny; 1266 final ASN1OctetString subFinal; 1267 final String matchingRuleID; 1268 final boolean dnAttributes; 1269 1270 if (startPos >= endPos) 1271 { 1272 throw new LDAPException(ResultCode.FILTER_ERROR, 1273 ERR_FILTER_TOO_SHORT.get(filterString)); 1274 } 1275 1276 int l = startPos; 1277 int r = endPos; 1278 1279 // First, see if the provided filter string is enclosed in parentheses, like 1280 // it should be. If so, then strip off the outer parentheses. 1281 if (filterString.charAt(l) == '(') 1282 { 1283 if (filterString.charAt(r) == ')') 1284 { 1285 l++; 1286 r--; 1287 } 1288 else 1289 { 1290 throw new LDAPException(ResultCode.FILTER_ERROR, 1291 ERR_FILTER_OPEN_WITHOUT_CLOSE.get(filterString, l, r)); 1292 } 1293 } 1294 else 1295 { 1296 // This is technically an error, and it's a bad practice. If we're 1297 // working on the complete filter string then we'll let it slide, but 1298 // otherwise we'll raise an error. 1299 if (l != 0) 1300 { 1301 throw new LDAPException(ResultCode.FILTER_ERROR, 1302 ERR_FILTER_MISSING_PARENTHESES.get(filterString, 1303 filterString.substring(l, r+1))); 1304 } 1305 } 1306 1307 1308 // Look at the first character of the filter to see if it's an '&', '|', or 1309 // '!'. If we find a parenthesis, then that's an error. 1310 switch (filterString.charAt(l)) 1311 { 1312 case '&': 1313 filterType = FILTER_TYPE_AND; 1314 filterComps = parseFilterComps(filterString, l+1, r, depth+1); 1315 notComp = null; 1316 attrName = null; 1317 assertionValue = null; 1318 subInitial = null; 1319 subAny = NO_SUB_ANY; 1320 subFinal = null; 1321 matchingRuleID = null; 1322 dnAttributes = false; 1323 break; 1324 1325 case '|': 1326 filterType = FILTER_TYPE_OR; 1327 filterComps = parseFilterComps(filterString, l+1, r, depth+1); 1328 notComp = null; 1329 attrName = null; 1330 assertionValue = null; 1331 subInitial = null; 1332 subAny = NO_SUB_ANY; 1333 subFinal = null; 1334 matchingRuleID = null; 1335 dnAttributes = false; 1336 break; 1337 1338 case '!': 1339 filterType = FILTER_TYPE_NOT; 1340 filterComps = NO_FILTERS; 1341 notComp = create(filterString, l+1, r, depth+1); 1342 attrName = null; 1343 assertionValue = null; 1344 subInitial = null; 1345 subAny = NO_SUB_ANY; 1346 subFinal = null; 1347 matchingRuleID = null; 1348 dnAttributes = false; 1349 break; 1350 1351 case '(': 1352 throw new LDAPException(ResultCode.FILTER_ERROR, 1353 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l)); 1354 1355 case ':': 1356 // This must be an extensible matching filter that starts with a 1357 // dnAttributes flag and/or matching rule ID, and we should parse it 1358 // accordingly. 1359 filterType = FILTER_TYPE_EXTENSIBLE_MATCH; 1360 filterComps = NO_FILTERS; 1361 notComp = null; 1362 attrName = null; 1363 subInitial = null; 1364 subAny = NO_SUB_ANY; 1365 subFinal = null; 1366 1367 // The next element must be either the "dn:{matchingruleid}" or just 1368 // "{matchingruleid}", and it must be followed by a colon. 1369 final int dnMRIDStart = ++l; 1370 while ((l <= r) && (filterString.charAt(l) != ':')) 1371 { 1372 l++; 1373 } 1374 1375 if (l > r) 1376 { 1377 throw new LDAPException(ResultCode.FILTER_ERROR, 1378 ERR_FILTER_NO_COLON_AFTER_MRID.get(filterString, startPos)); 1379 } 1380 else if (l == dnMRIDStart) 1381 { 1382 throw new LDAPException(ResultCode.FILTER_ERROR, 1383 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1384 } 1385 final String s = filterString.substring(dnMRIDStart, l++); 1386 if (s.equalsIgnoreCase("dn")) 1387 { 1388 dnAttributes = true; 1389 1390 // The colon must be followed by the matching rule ID and another 1391 // colon. 1392 final int mrIDStart = l; 1393 while ((l < r) && (filterString.charAt(l) != ':')) 1394 { 1395 l++; 1396 } 1397 1398 if (l >= r) 1399 { 1400 throw new LDAPException(ResultCode.FILTER_ERROR, 1401 ERR_FILTER_NO_COLON_AFTER_MRID.get(filterString, startPos)); 1402 } 1403 1404 matchingRuleID = filterString.substring(mrIDStart, l); 1405 if (matchingRuleID.isEmpty()) 1406 { 1407 throw new LDAPException(ResultCode.FILTER_ERROR, 1408 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1409 } 1410 1411 if ((++l > r) || (filterString.charAt(l) != '=')) 1412 { 1413 throw new LDAPException(ResultCode.FILTER_ERROR, 1414 ERR_FILTER_UNEXPECTED_CHAR_AFTER_MRID.get(filterString, 1415 startPos, filterString.charAt(l))); 1416 } 1417 } 1418 else 1419 { 1420 matchingRuleID = s; 1421 dnAttributes = false; 1422 1423 // The colon must be followed by an equal sign. 1424 if ((l > r) || (filterString.charAt(l) != '=')) 1425 { 1426 throw new LDAPException(ResultCode.FILTER_ERROR, 1427 ERR_FILTER_NO_EQUAL_AFTER_MRID.get(filterString, startPos)); 1428 } 1429 } 1430 1431 // Now we should be able to read the value, handling any escape 1432 // characters as we go. 1433 l++; 1434 final ByteStringBuffer valueBuffer = new ByteStringBuffer(r - l + 1); 1435 while (l <= r) 1436 { 1437 final char c = filterString.charAt(l); 1438 if (c == '\\') 1439 { 1440 l = readEscapedHexString(filterString, ++l, valueBuffer); 1441 } 1442 else if (c == '(') 1443 { 1444 throw new LDAPException(ResultCode.FILTER_ERROR, 1445 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l)); 1446 } 1447 else if (c == ')') 1448 { 1449 throw new LDAPException(ResultCode.FILTER_ERROR, 1450 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(filterString, l)); 1451 } 1452 else 1453 { 1454 valueBuffer.append(c); 1455 l++; 1456 } 1457 } 1458 assertionValue = new ASN1OctetString(valueBuffer.toByteArray()); 1459 break; 1460 1461 1462 default: 1463 // We know that it's not an AND, OR, or NOT filter, so we can eliminate 1464 // the variables used only for them. 1465 filterComps = NO_FILTERS; 1466 notComp = null; 1467 1468 1469 // We should now be able to read a non-empty attribute name. 1470 final int attrStartPos = l; 1471 int attrEndPos = -1; 1472 byte tempFilterType = 0x00; 1473 boolean filterTypeKnown = false; 1474 boolean equalFound = false; 1475attrNameLoop: 1476 while (l <= r) 1477 { 1478 final char c = filterString.charAt(l++); 1479 switch (c) 1480 { 1481 case ':': 1482 tempFilterType = FILTER_TYPE_EXTENSIBLE_MATCH; 1483 filterTypeKnown = true; 1484 attrEndPos = l - 1; 1485 break attrNameLoop; 1486 1487 case '>': 1488 tempFilterType = FILTER_TYPE_GREATER_OR_EQUAL; 1489 filterTypeKnown = true; 1490 attrEndPos = l - 1; 1491 1492 if (l <= r) 1493 { 1494 if (filterString.charAt(l++) != '=') 1495 { 1496 throw new LDAPException(ResultCode.FILTER_ERROR, 1497 ERR_FILTER_UNEXPECTED_CHAR_AFTER_GT.get(filterString, 1498 startPos, filterString.charAt(l-1))); 1499 } 1500 } 1501 else 1502 { 1503 throw new LDAPException(ResultCode.FILTER_ERROR, 1504 ERR_FILTER_END_AFTER_GT.get(filterString, startPos)); 1505 } 1506 break attrNameLoop; 1507 1508 case '<': 1509 tempFilterType = FILTER_TYPE_LESS_OR_EQUAL; 1510 filterTypeKnown = true; 1511 attrEndPos = l - 1; 1512 1513 if (l <= r) 1514 { 1515 if (filterString.charAt(l++) != '=') 1516 { 1517 throw new LDAPException(ResultCode.FILTER_ERROR, 1518 ERR_FILTER_UNEXPECTED_CHAR_AFTER_LT.get(filterString, 1519 startPos, filterString.charAt(l-1))); 1520 } 1521 } 1522 else 1523 { 1524 throw new LDAPException(ResultCode.FILTER_ERROR, 1525 ERR_FILTER_END_AFTER_LT.get(filterString, startPos)); 1526 } 1527 break attrNameLoop; 1528 1529 case '~': 1530 tempFilterType = FILTER_TYPE_APPROXIMATE_MATCH; 1531 filterTypeKnown = true; 1532 attrEndPos = l - 1; 1533 1534 if (l <= r) 1535 { 1536 if (filterString.charAt(l++) != '=') 1537 { 1538 throw new LDAPException(ResultCode.FILTER_ERROR, 1539 ERR_FILTER_UNEXPECTED_CHAR_AFTER_TILDE.get(filterString, 1540 startPos, filterString.charAt(l-1))); 1541 } 1542 } 1543 else 1544 { 1545 throw new LDAPException(ResultCode.FILTER_ERROR, 1546 ERR_FILTER_END_AFTER_TILDE.get(filterString, startPos)); 1547 } 1548 break attrNameLoop; 1549 1550 case '=': 1551 // It could be either an equality, presence, or substring filter. 1552 // We'll need to look at the value to determine that. 1553 attrEndPos = l - 1; 1554 equalFound = true; 1555 break attrNameLoop; 1556 } 1557 } 1558 1559 if (attrEndPos <= attrStartPos) 1560 { 1561 if (equalFound) 1562 { 1563 throw new LDAPException(ResultCode.FILTER_ERROR, 1564 ERR_FILTER_EMPTY_ATTR_NAME.get(filterString, startPos)); 1565 } 1566 else 1567 { 1568 throw new LDAPException(ResultCode.FILTER_ERROR, 1569 ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos)); 1570 } 1571 } 1572 attrName = filterString.substring(attrStartPos, attrEndPos); 1573 1574 1575 // See if we're dealing with an extensible match filter. If so, then 1576 // we may still need to do additional parsing to get the matching rule 1577 // ID and/or the dnAttributes flag. Otherwise, we can rule out any 1578 // variables that are specific to extensible matching filters. 1579 if (filterTypeKnown && (tempFilterType == FILTER_TYPE_EXTENSIBLE_MATCH)) 1580 { 1581 if (l > r) 1582 { 1583 throw new LDAPException(ResultCode.FILTER_ERROR, 1584 ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos)); 1585 } 1586 1587 final char c = filterString.charAt(l++); 1588 if (c == '=') 1589 { 1590 matchingRuleID = null; 1591 dnAttributes = false; 1592 } 1593 else 1594 { 1595 // We have either a matching rule ID or a dnAttributes flag, or 1596 // both. Iterate through the filter until we find the equal sign, 1597 // and then figure out what we have from that. 1598 equalFound = false; 1599 final int substrStartPos = l - 1; 1600 while (l <= r) 1601 { 1602 if (filterString.charAt(l++) == '=') 1603 { 1604 equalFound = true; 1605 break; 1606 } 1607 } 1608 1609 if (! equalFound) 1610 { 1611 throw new LDAPException(ResultCode.FILTER_ERROR, 1612 ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos)); 1613 } 1614 1615 final String substr = filterString.substring(substrStartPos, l-1); 1616 final String lowerSubstr = StaticUtils.toLowerCase(substr); 1617 if (! substr.endsWith(":")) 1618 { 1619 throw new LDAPException(ResultCode.FILTER_ERROR, 1620 ERR_FILTER_CANNOT_PARSE_MRID.get(filterString, startPos)); 1621 } 1622 1623 if (lowerSubstr.equals("dn:")) 1624 { 1625 matchingRuleID = null; 1626 dnAttributes = true; 1627 } 1628 else if (lowerSubstr.startsWith("dn:")) 1629 { 1630 matchingRuleID = substr.substring(3, substr.length() - 1); 1631 if (matchingRuleID.isEmpty()) 1632 { 1633 throw new LDAPException(ResultCode.FILTER_ERROR, 1634 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1635 } 1636 1637 dnAttributes = true; 1638 } 1639 else 1640 { 1641 matchingRuleID = substr.substring(0, substr.length() - 1); 1642 dnAttributes = false; 1643 1644 if (matchingRuleID.isEmpty()) 1645 { 1646 throw new LDAPException(ResultCode.FILTER_ERROR, 1647 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1648 } 1649 } 1650 } 1651 } 1652 else 1653 { 1654 matchingRuleID = null; 1655 dnAttributes = false; 1656 } 1657 1658 1659 // At this point, we're ready to read the value. If we still don't 1660 // know what type of filter we're dealing with, then we can tell that 1661 // based on asterisks in the value. 1662 if (l > r) 1663 { 1664 assertionValue = new ASN1OctetString(); 1665 if (! filterTypeKnown) 1666 { 1667 tempFilterType = FILTER_TYPE_EQUALITY; 1668 } 1669 1670 subInitial = null; 1671 subAny = NO_SUB_ANY; 1672 subFinal = null; 1673 } 1674 else if (l == r) 1675 { 1676 if (filterTypeKnown) 1677 { 1678 switch (filterString.charAt(l)) 1679 { 1680 case '*': 1681 case '(': 1682 case ')': 1683 case '\\': 1684 throw new LDAPException(ResultCode.FILTER_ERROR, 1685 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(filterString, 1686 startPos, filterString.charAt(l))); 1687 } 1688 1689 assertionValue = 1690 new ASN1OctetString(filterString.substring(l, l+1)); 1691 } 1692 else 1693 { 1694 final char c = filterString.charAt(l); 1695 switch (c) 1696 { 1697 case '*': 1698 tempFilterType = FILTER_TYPE_PRESENCE; 1699 assertionValue = null; 1700 break; 1701 1702 case '\\': 1703 case '(': 1704 case ')': 1705 throw new LDAPException(ResultCode.FILTER_ERROR, 1706 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(filterString, 1707 startPos, filterString.charAt(l))); 1708 1709 default: 1710 tempFilterType = FILTER_TYPE_EQUALITY; 1711 assertionValue = 1712 new ASN1OctetString(filterString.substring(l, l+1)); 1713 break; 1714 } 1715 } 1716 1717 subInitial = null; 1718 subAny = NO_SUB_ANY; 1719 subFinal = null; 1720 } 1721 else 1722 { 1723 if (! filterTypeKnown) 1724 { 1725 tempFilterType = FILTER_TYPE_EQUALITY; 1726 } 1727 1728 final int valueStartPos = l; 1729 ASN1OctetString tempSubInitial = null; 1730 ASN1OctetString tempSubFinal = null; 1731 final ArrayList<ASN1OctetString> subAnyList = new ArrayList<>(1); 1732 ByteStringBuffer buffer = new ByteStringBuffer(r - l + 1); 1733 while (l <= r) 1734 { 1735 final char c = filterString.charAt(l++); 1736 switch (c) 1737 { 1738 case '*': 1739 if (filterTypeKnown) 1740 { 1741 throw new LDAPException(ResultCode.FILTER_ERROR, 1742 ERR_FILTER_UNEXPECTED_ASTERISK.get(filterString, 1743 startPos)); 1744 } 1745 else 1746 { 1747 if ((l-1) == valueStartPos) 1748 { 1749 // The first character is an asterisk, so there is no 1750 // subInitial. 1751 } 1752 else 1753 { 1754 if (tempFilterType == FILTER_TYPE_SUBSTRING) 1755 { 1756 // We already know that it's a substring filter, so this 1757 // must be a subAny portion. However, if the buffer is 1758 // empty, then that means that there were two asterisks 1759 // right next to each other, which is invalid. 1760 if (buffer.length() == 0) 1761 { 1762 throw new LDAPException(ResultCode.FILTER_ERROR, 1763 ERR_FILTER_UNEXPECTED_DOUBLE_ASTERISK.get( 1764 filterString, startPos)); 1765 } 1766 else 1767 { 1768 subAnyList.add( 1769 new ASN1OctetString(buffer.toByteArray())); 1770 buffer = new ByteStringBuffer(r - l + 1); 1771 } 1772 } 1773 else 1774 { 1775 // We haven't yet set the filter type, so the buffer must 1776 // contain the subInitial portion. We also know it's not 1777 // empty because of an earlier check. 1778 tempSubInitial = 1779 new ASN1OctetString(buffer.toByteArray()); 1780 buffer = new ByteStringBuffer(r - l + 1); 1781 } 1782 } 1783 1784 tempFilterType = FILTER_TYPE_SUBSTRING; 1785 } 1786 break; 1787 1788 case '\\': 1789 l = readEscapedHexString(filterString, l, buffer); 1790 break; 1791 1792 case '(': 1793 throw new LDAPException(ResultCode.FILTER_ERROR, 1794 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l)); 1795 1796 case ')': 1797 throw new LDAPException(ResultCode.FILTER_ERROR, 1798 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(filterString, l)); 1799 1800 default: 1801 if (Character.isHighSurrogate(c)) 1802 { 1803 if (l <= r) 1804 { 1805 final char c2 = filterString.charAt(l); 1806 if (Character.isLowSurrogate(c2)) 1807 { 1808 l++; 1809 final int codePoint = Character.toCodePoint(c, c2); 1810 buffer.append(new String(new int[] { codePoint }, 0, 1)); 1811 break; 1812 } 1813 } 1814 } 1815 1816 buffer.append(c); 1817 break; 1818 } 1819 } 1820 1821 if ((tempFilterType == FILTER_TYPE_SUBSTRING) && 1822 (! buffer.isEmpty())) 1823 { 1824 // The buffer must contain the subFinal portion. 1825 tempSubFinal = new ASN1OctetString(buffer.toByteArray()); 1826 } 1827 1828 subInitial = tempSubInitial; 1829 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 1830 subFinal = tempSubFinal; 1831 1832 if (tempFilterType == FILTER_TYPE_SUBSTRING) 1833 { 1834 assertionValue = null; 1835 } 1836 else 1837 { 1838 assertionValue = new ASN1OctetString(buffer.toByteArray()); 1839 } 1840 } 1841 1842 filterType = tempFilterType; 1843 break; 1844 } 1845 1846 1847 if (startPos == 0) 1848 { 1849 return new Filter(filterString, filterType, filterComps, notComp, 1850 attrName, assertionValue, subInitial, subAny, subFinal, 1851 matchingRuleID, dnAttributes); 1852 } 1853 else 1854 { 1855 return new Filter(filterString.substring(startPos, endPos+1), filterType, 1856 filterComps, notComp, attrName, assertionValue, 1857 subInitial, subAny, subFinal, matchingRuleID, 1858 dnAttributes); 1859 } 1860 } 1861 1862 1863 1864 /** 1865 * Parses the specified portion of the provided filter string to obtain a set 1866 * of filter components for use in an AND or OR filter. 1867 * 1868 * @param filterString The string representation for the set of filters. 1869 * @param startPos The position of the first character to consider as 1870 * part of the first filter. 1871 * @param endPos The position of the last character to consider as 1872 * part of the last filter. 1873 * @param depth The current nesting depth for this filter. It should 1874 * be increased by one for each AND, OR, or NOT filter 1875 * encountered, in order to prevent stack overflow 1876 * errors from excessive recursion. 1877 * 1878 * @return The decoded set of search filters. 1879 * 1880 * @throws LDAPException If the provided string cannot be decoded as a set 1881 * of LDAP search filters. 1882 */ 1883 private static Filter[] parseFilterComps(final String filterString, 1884 final int startPos, final int endPos, 1885 final int depth) 1886 throws LDAPException 1887 { 1888 if (startPos > endPos) 1889 { 1890 // This is acceptable, since it can represent an LDAP TRUE or FALSE filter 1891 // as described in RFC 4526. 1892 return NO_FILTERS; 1893 } 1894 1895 1896 // The set of filters must start with an opening parenthesis, and end with a 1897 // closing parenthesis. 1898 if (filterString.charAt(startPos) != '(') 1899 { 1900 throw new LDAPException(ResultCode.FILTER_ERROR, 1901 ERR_FILTER_EXPECTED_OPEN_PAREN.get(filterString, startPos)); 1902 } 1903 if (filterString.charAt(endPos) != ')') 1904 { 1905 throw new LDAPException(ResultCode.FILTER_ERROR, 1906 ERR_FILTER_EXPECTED_CLOSE_PAREN.get(filterString, startPos)); 1907 } 1908 1909 1910 // Iterate through the specified portion of the filter string and count 1911 // opening and closing parentheses to figure out where one filter ends and 1912 // another begins. 1913 final ArrayList<Filter> filterList = new ArrayList<>(5); 1914 int filterStartPos = startPos; 1915 int pos = startPos; 1916 int numOpen = 0; 1917 while (pos <= endPos) 1918 { 1919 final char c = filterString.charAt(pos++); 1920 if (c == '(') 1921 { 1922 numOpen++; 1923 } 1924 else if (c == ')') 1925 { 1926 numOpen--; 1927 if (numOpen == 0) 1928 { 1929 filterList.add(create(filterString, filterStartPos, pos-1, depth)); 1930 filterStartPos = pos; 1931 } 1932 } 1933 } 1934 1935 if (numOpen != 0) 1936 { 1937 throw new LDAPException(ResultCode.FILTER_ERROR, 1938 ERR_FILTER_MISMATCHED_PARENS.get(filterString, startPos, endPos)); 1939 } 1940 1941 return filterList.toArray(new Filter[filterList.size()]); 1942 } 1943 1944 1945 1946 /** 1947 * Reads one or more hex-encoded bytes from the specified portion of the 1948 * filter string. 1949 * 1950 * @param filterString The string from which the data is to be read. 1951 * @param startPos The position at which to start reading. This should 1952 * be the position of first hex character immediately 1953 * after the initial backslash. 1954 * @param buffer The buffer to which the decoded string portion should 1955 * be appended. 1956 * 1957 * @return The position at which the caller may resume parsing. 1958 * 1959 * @throws LDAPException If a problem occurs while reading hex-encoded 1960 * bytes. 1961 */ 1962 private static int readEscapedHexString(final String filterString, 1963 final int startPos, 1964 final ByteStringBuffer buffer) 1965 throws LDAPException 1966 { 1967 final byte b; 1968 switch (filterString.charAt(startPos)) 1969 { 1970 case '0': 1971 b = 0x00; 1972 break; 1973 case '1': 1974 b = 0x10; 1975 break; 1976 case '2': 1977 b = 0x20; 1978 break; 1979 case '3': 1980 b = 0x30; 1981 break; 1982 case '4': 1983 b = 0x40; 1984 break; 1985 case '5': 1986 b = 0x50; 1987 break; 1988 case '6': 1989 b = 0x60; 1990 break; 1991 case '7': 1992 b = 0x70; 1993 break; 1994 case '8': 1995 b = (byte) 0x80; 1996 break; 1997 case '9': 1998 b = (byte) 0x90; 1999 break; 2000 case 'a': 2001 case 'A': 2002 b = (byte) 0xA0; 2003 break; 2004 case 'b': 2005 case 'B': 2006 b = (byte) 0xB0; 2007 break; 2008 case 'c': 2009 case 'C': 2010 b = (byte) 0xC0; 2011 break; 2012 case 'd': 2013 case 'D': 2014 b = (byte) 0xD0; 2015 break; 2016 case 'e': 2017 case 'E': 2018 b = (byte) 0xE0; 2019 break; 2020 case 'f': 2021 case 'F': 2022 b = (byte) 0xF0; 2023 break; 2024 default: 2025 throw new LDAPException(ResultCode.FILTER_ERROR, 2026 ERR_FILTER_INVALID_HEX_CHAR.get(filterString, 2027 filterString.charAt(startPos), startPos)); 2028 } 2029 2030 switch (filterString.charAt(startPos+1)) 2031 { 2032 case '0': 2033 buffer.append(b); 2034 break; 2035 case '1': 2036 buffer.append((byte) (b | 0x01)); 2037 break; 2038 case '2': 2039 buffer.append((byte) (b | 0x02)); 2040 break; 2041 case '3': 2042 buffer.append((byte) (b | 0x03)); 2043 break; 2044 case '4': 2045 buffer.append((byte) (b | 0x04)); 2046 break; 2047 case '5': 2048 buffer.append((byte) (b | 0x05)); 2049 break; 2050 case '6': 2051 buffer.append((byte) (b | 0x06)); 2052 break; 2053 case '7': 2054 buffer.append((byte) (b | 0x07)); 2055 break; 2056 case '8': 2057 buffer.append((byte) (b | 0x08)); 2058 break; 2059 case '9': 2060 buffer.append((byte) (b | 0x09)); 2061 break; 2062 case 'a': 2063 case 'A': 2064 buffer.append((byte) (b | 0x0A)); 2065 break; 2066 case 'b': 2067 case 'B': 2068 buffer.append((byte) (b | 0x0B)); 2069 break; 2070 case 'c': 2071 case 'C': 2072 buffer.append((byte) (b | 0x0C)); 2073 break; 2074 case 'd': 2075 case 'D': 2076 buffer.append((byte) (b | 0x0D)); 2077 break; 2078 case 'e': 2079 case 'E': 2080 buffer.append((byte) (b | 0x0E)); 2081 break; 2082 case 'f': 2083 case 'F': 2084 buffer.append((byte) (b | 0x0F)); 2085 break; 2086 default: 2087 throw new LDAPException(ResultCode.FILTER_ERROR, 2088 ERR_FILTER_INVALID_HEX_CHAR.get(filterString, 2089 filterString.charAt(startPos+1), (startPos+1))); 2090 } 2091 2092 return startPos+2; 2093 } 2094 2095 2096 2097 /** 2098 * Writes an ASN.1-encoded representation of this filter to the provided ASN.1 2099 * buffer. 2100 * 2101 * @param buffer The ASN.1 buffer to which the encoded representation should 2102 * be written. 2103 */ 2104 public void writeTo(final ASN1Buffer buffer) 2105 { 2106 switch (filterType) 2107 { 2108 case FILTER_TYPE_AND: 2109 case FILTER_TYPE_OR: 2110 final ASN1BufferSet compSet = buffer.beginSet(filterType); 2111 for (final Filter f : filterComps) 2112 { 2113 f.writeTo(buffer); 2114 } 2115 compSet.end(); 2116 break; 2117 2118 case FILTER_TYPE_NOT: 2119 buffer.addElement( 2120 new ASN1Element(filterType, notComp.encode().encode())); 2121 break; 2122 2123 case FILTER_TYPE_EQUALITY: 2124 case FILTER_TYPE_GREATER_OR_EQUAL: 2125 case FILTER_TYPE_LESS_OR_EQUAL: 2126 case FILTER_TYPE_APPROXIMATE_MATCH: 2127 final ASN1BufferSequence avaSequence = buffer.beginSequence(filterType); 2128 buffer.addOctetString(attrName); 2129 buffer.addElement(assertionValue); 2130 avaSequence.end(); 2131 break; 2132 2133 case FILTER_TYPE_SUBSTRING: 2134 final ASN1BufferSequence subFilterSequence = 2135 buffer.beginSequence(filterType); 2136 buffer.addOctetString(attrName); 2137 2138 final ASN1BufferSequence valueSequence = buffer.beginSequence(); 2139 if (subInitial != null) 2140 { 2141 buffer.addOctetString(SUBSTRING_TYPE_SUBINITIAL, 2142 subInitial.getValue()); 2143 } 2144 2145 for (final ASN1OctetString s : subAny) 2146 { 2147 buffer.addOctetString(SUBSTRING_TYPE_SUBANY, s.getValue()); 2148 } 2149 2150 if (subFinal != null) 2151 { 2152 buffer.addOctetString(SUBSTRING_TYPE_SUBFINAL, subFinal.getValue()); 2153 } 2154 valueSequence.end(); 2155 subFilterSequence.end(); 2156 break; 2157 2158 case FILTER_TYPE_PRESENCE: 2159 buffer.addOctetString(filterType, attrName); 2160 break; 2161 2162 case FILTER_TYPE_EXTENSIBLE_MATCH: 2163 final ASN1BufferSequence mrSequence = buffer.beginSequence(filterType); 2164 if (matchingRuleID != null) 2165 { 2166 buffer.addOctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID, 2167 matchingRuleID); 2168 } 2169 2170 if (attrName != null) 2171 { 2172 buffer.addOctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName); 2173 } 2174 2175 buffer.addOctetString(EXTENSIBLE_TYPE_MATCH_VALUE, 2176 assertionValue.getValue()); 2177 2178 if (dnAttributes) 2179 { 2180 buffer.addBoolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, true); 2181 } 2182 mrSequence.end(); 2183 break; 2184 } 2185 } 2186 2187 2188 2189 /** 2190 * Encodes this search filter to an ASN.1 element suitable for inclusion in an 2191 * LDAP search request protocol op. 2192 * 2193 * @return An ASN.1 element containing the encoded search filter. 2194 */ 2195 public ASN1Element encode() 2196 { 2197 switch (filterType) 2198 { 2199 case FILTER_TYPE_AND: 2200 case FILTER_TYPE_OR: 2201 final ASN1Element[] filterElements = 2202 new ASN1Element[filterComps.length]; 2203 for (int i=0; i < filterComps.length; i++) 2204 { 2205 filterElements[i] = filterComps[i].encode(); 2206 } 2207 return new ASN1Set(filterType, filterElements); 2208 2209 2210 case FILTER_TYPE_NOT: 2211 return new ASN1Element(filterType, notComp.encode().encode()); 2212 2213 2214 case FILTER_TYPE_EQUALITY: 2215 case FILTER_TYPE_GREATER_OR_EQUAL: 2216 case FILTER_TYPE_LESS_OR_EQUAL: 2217 case FILTER_TYPE_APPROXIMATE_MATCH: 2218 final ASN1OctetString[] attrValueAssertionElements = 2219 { 2220 new ASN1OctetString(attrName), 2221 assertionValue 2222 }; 2223 return new ASN1Sequence(filterType, attrValueAssertionElements); 2224 2225 2226 case FILTER_TYPE_SUBSTRING: 2227 final ArrayList<ASN1OctetString> subList = 2228 new ArrayList<>(2 + subAny.length); 2229 if (subInitial != null) 2230 { 2231 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL, 2232 subInitial.getValue())); 2233 } 2234 2235 for (final ASN1Element subAnyElement : subAny) 2236 { 2237 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBANY, 2238 subAnyElement.getValue())); 2239 } 2240 2241 2242 if (subFinal != null) 2243 { 2244 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, 2245 subFinal.getValue())); 2246 } 2247 2248 final ASN1Element[] subFilterElements = 2249 { 2250 new ASN1OctetString(attrName), 2251 new ASN1Sequence(subList) 2252 }; 2253 return new ASN1Sequence(filterType, subFilterElements); 2254 2255 2256 case FILTER_TYPE_PRESENCE: 2257 return new ASN1OctetString(filterType, attrName); 2258 2259 2260 case FILTER_TYPE_EXTENSIBLE_MATCH: 2261 final ArrayList<ASN1Element> emElementList = new ArrayList<>(4); 2262 if (matchingRuleID != null) 2263 { 2264 emElementList.add(new ASN1OctetString( 2265 EXTENSIBLE_TYPE_MATCHING_RULE_ID, matchingRuleID)); 2266 } 2267 2268 if (attrName != null) 2269 { 2270 emElementList.add(new ASN1OctetString( 2271 EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName)); 2272 } 2273 2274 emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, 2275 assertionValue.getValue())); 2276 2277 if (dnAttributes) 2278 { 2279 emElementList.add(new ASN1Boolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, 2280 true)); 2281 } 2282 2283 return new ASN1Sequence(filterType, emElementList); 2284 2285 2286 default: 2287 throw new AssertionError(ERR_FILTER_INVALID_TYPE.get( 2288 StaticUtils.toHex(filterType))); 2289 } 2290 } 2291 2292 2293 2294 /** 2295 * Reads and decodes a search filter from the provided ASN.1 stream reader. 2296 * 2297 * @param reader The ASN.1 stream reader from which to read the filter. 2298 * 2299 * @return The decoded search filter. 2300 * 2301 * @throws LDAPException If an error occurs while reading or parsing the 2302 * search filter. 2303 */ 2304 public static Filter readFrom(final ASN1StreamReader reader) 2305 throws LDAPException 2306 { 2307 try 2308 { 2309 final Filter[] filterComps; 2310 final Filter notComp; 2311 final String attrName; 2312 final ASN1OctetString assertionValue; 2313 final ASN1OctetString subInitial; 2314 final ASN1OctetString[] subAny; 2315 final ASN1OctetString subFinal; 2316 final String matchingRuleID; 2317 final boolean dnAttributes; 2318 2319 final byte filterType = (byte) reader.peek(); 2320 2321 switch (filterType) 2322 { 2323 case FILTER_TYPE_AND: 2324 case FILTER_TYPE_OR: 2325 final ArrayList<Filter> comps = new ArrayList<>(5); 2326 final ASN1StreamReaderSet elementSet = reader.beginSet(); 2327 while (elementSet.hasMoreElements()) 2328 { 2329 comps.add(readFrom(reader)); 2330 } 2331 2332 filterComps = new Filter[comps.size()]; 2333 comps.toArray(filterComps); 2334 2335 notComp = null; 2336 attrName = null; 2337 assertionValue = null; 2338 subInitial = null; 2339 subAny = NO_SUB_ANY; 2340 subFinal = null; 2341 matchingRuleID = null; 2342 dnAttributes = false; 2343 break; 2344 2345 2346 case FILTER_TYPE_NOT: 2347 final ASN1Element notFilterElement; 2348 try 2349 { 2350 final ASN1Element e = reader.readElement(); 2351 notFilterElement = ASN1Element.decode(e.getValue()); 2352 } 2353 catch (final ASN1Exception ae) 2354 { 2355 Debug.debugException(ae); 2356 throw new LDAPException(ResultCode.DECODING_ERROR, 2357 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get( 2358 StaticUtils.getExceptionMessage(ae)), 2359 ae); 2360 } 2361 notComp = decode(notFilterElement); 2362 2363 filterComps = NO_FILTERS; 2364 attrName = null; 2365 assertionValue = null; 2366 subInitial = null; 2367 subAny = NO_SUB_ANY; 2368 subFinal = null; 2369 matchingRuleID = null; 2370 dnAttributes = false; 2371 break; 2372 2373 2374 case FILTER_TYPE_EQUALITY: 2375 case FILTER_TYPE_GREATER_OR_EQUAL: 2376 case FILTER_TYPE_LESS_OR_EQUAL: 2377 case FILTER_TYPE_APPROXIMATE_MATCH: 2378 reader.beginSequence(); 2379 attrName = reader.readString(); 2380 assertionValue = new ASN1OctetString(reader.readBytes()); 2381 2382 filterComps = NO_FILTERS; 2383 notComp = null; 2384 subInitial = null; 2385 subAny = NO_SUB_ANY; 2386 subFinal = null; 2387 matchingRuleID = null; 2388 dnAttributes = false; 2389 break; 2390 2391 2392 case FILTER_TYPE_SUBSTRING: 2393 reader.beginSequence(); 2394 attrName = reader.readString(); 2395 2396 ASN1OctetString tempSubInitial = null; 2397 ASN1OctetString tempSubFinal = null; 2398 final ArrayList<ASN1OctetString> subAnyList = new ArrayList<>(1); 2399 final ASN1StreamReaderSequence subSequence = reader.beginSequence(); 2400 while (subSequence.hasMoreElements()) 2401 { 2402 final byte type = (byte) reader.peek(); 2403 final ASN1OctetString s = 2404 new ASN1OctetString(type, reader.readBytes()); 2405 switch (type) 2406 { 2407 case SUBSTRING_TYPE_SUBINITIAL: 2408 tempSubInitial = s; 2409 break; 2410 case SUBSTRING_TYPE_SUBANY: 2411 subAnyList.add(s); 2412 break; 2413 case SUBSTRING_TYPE_SUBFINAL: 2414 tempSubFinal = s; 2415 break; 2416 default: 2417 throw new LDAPException(ResultCode.DECODING_ERROR, 2418 ERR_FILTER_INVALID_SUBSTR_TYPE.get( 2419 StaticUtils.toHex(type))); 2420 } 2421 } 2422 2423 subInitial = tempSubInitial; 2424 subFinal = tempSubFinal; 2425 2426 subAny = new ASN1OctetString[subAnyList.size()]; 2427 subAnyList.toArray(subAny); 2428 2429 filterComps = NO_FILTERS; 2430 notComp = null; 2431 assertionValue = null; 2432 matchingRuleID = null; 2433 dnAttributes = false; 2434 break; 2435 2436 2437 case FILTER_TYPE_PRESENCE: 2438 attrName = reader.readString(); 2439 2440 filterComps = NO_FILTERS; 2441 notComp = null; 2442 assertionValue = null; 2443 subInitial = null; 2444 subAny = NO_SUB_ANY; 2445 subFinal = null; 2446 matchingRuleID = null; 2447 dnAttributes = false; 2448 break; 2449 2450 2451 case FILTER_TYPE_EXTENSIBLE_MATCH: 2452 String tempAttrName = null; 2453 ASN1OctetString tempAssertionValue = null; 2454 String tempMatchingRuleID = null; 2455 boolean tempDNAttributes = false; 2456 2457 final ASN1StreamReaderSequence emSequence = reader.beginSequence(); 2458 while (emSequence.hasMoreElements()) 2459 { 2460 final byte type = (byte) reader.peek(); 2461 switch (type) 2462 { 2463 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 2464 tempAttrName = reader.readString(); 2465 break; 2466 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 2467 tempMatchingRuleID = reader.readString(); 2468 break; 2469 case EXTENSIBLE_TYPE_MATCH_VALUE: 2470 tempAssertionValue = 2471 new ASN1OctetString(type, reader.readBytes()); 2472 break; 2473 case EXTENSIBLE_TYPE_DN_ATTRIBUTES: 2474 tempDNAttributes = reader.readBoolean(); 2475 break; 2476 default: 2477 throw new LDAPException(ResultCode.DECODING_ERROR, 2478 ERR_FILTER_EXTMATCH_INVALID_TYPE.get( 2479 StaticUtils.toHex(type))); 2480 } 2481 } 2482 2483 if ((tempAttrName == null) && (tempMatchingRuleID == null)) 2484 { 2485 throw new LDAPException(ResultCode.DECODING_ERROR, 2486 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get()); 2487 } 2488 2489 if (tempAssertionValue == null) 2490 { 2491 throw new LDAPException(ResultCode.DECODING_ERROR, 2492 ERR_FILTER_EXTMATCH_NO_VALUE.get()); 2493 } 2494 2495 attrName = tempAttrName; 2496 assertionValue = tempAssertionValue; 2497 matchingRuleID = tempMatchingRuleID; 2498 dnAttributes = tempDNAttributes; 2499 2500 filterComps = NO_FILTERS; 2501 notComp = null; 2502 subInitial = null; 2503 subAny = NO_SUB_ANY; 2504 subFinal = null; 2505 break; 2506 2507 2508 default: 2509 throw new LDAPException(ResultCode.DECODING_ERROR, 2510 ERR_FILTER_ELEMENT_INVALID_TYPE.get( 2511 StaticUtils.toHex(filterType))); 2512 } 2513 2514 return new Filter(null, filterType, filterComps, notComp, attrName, 2515 assertionValue, subInitial, subAny, subFinal, 2516 matchingRuleID, dnAttributes); 2517 } 2518 catch (final LDAPException le) 2519 { 2520 Debug.debugException(le); 2521 throw le; 2522 } 2523 catch (final Exception e) 2524 { 2525 Debug.debugException(e); 2526 throw new LDAPException(ResultCode.DECODING_ERROR, 2527 ERR_FILTER_CANNOT_DECODE.get(StaticUtils.getExceptionMessage(e)), e); 2528 } 2529 } 2530 2531 2532 2533 /** 2534 * Decodes the provided ASN.1 element as a search filter. 2535 * 2536 * @param filterElement The ASN.1 element containing the encoded search 2537 * filter. 2538 * 2539 * @return The decoded search filter. 2540 * 2541 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 2542 * a search filter. 2543 */ 2544 public static Filter decode(final ASN1Element filterElement) 2545 throws LDAPException 2546 { 2547 final byte filterType = filterElement.getType(); 2548 final Filter[] filterComps; 2549 final Filter notComp; 2550 final String attrName; 2551 final ASN1OctetString assertionValue; 2552 final ASN1OctetString subInitial; 2553 final ASN1OctetString[] subAny; 2554 final ASN1OctetString subFinal; 2555 final String matchingRuleID; 2556 final boolean dnAttributes; 2557 2558 switch (filterType) 2559 { 2560 case FILTER_TYPE_AND: 2561 case FILTER_TYPE_OR: 2562 notComp = null; 2563 attrName = null; 2564 assertionValue = null; 2565 subInitial = null; 2566 subAny = NO_SUB_ANY; 2567 subFinal = null; 2568 matchingRuleID = null; 2569 dnAttributes = false; 2570 2571 final ASN1Set compSet; 2572 try 2573 { 2574 compSet = ASN1Set.decodeAsSet(filterElement); 2575 } 2576 catch (final ASN1Exception ae) 2577 { 2578 Debug.debugException(ae); 2579 throw new LDAPException(ResultCode.DECODING_ERROR, 2580 ERR_FILTER_CANNOT_DECODE_COMPS.get( 2581 StaticUtils.getExceptionMessage(ae)), 2582 ae); 2583 } 2584 2585 final ASN1Element[] compElements = compSet.elements(); 2586 filterComps = new Filter[compElements.length]; 2587 for (int i=0; i < compElements.length; i++) 2588 { 2589 filterComps[i] = decode(compElements[i]); 2590 } 2591 break; 2592 2593 2594 case FILTER_TYPE_NOT: 2595 filterComps = NO_FILTERS; 2596 attrName = null; 2597 assertionValue = null; 2598 subInitial = null; 2599 subAny = NO_SUB_ANY; 2600 subFinal = null; 2601 matchingRuleID = null; 2602 dnAttributes = false; 2603 2604 final ASN1Element notFilterElement; 2605 try 2606 { 2607 notFilterElement = ASN1Element.decode(filterElement.getValue()); 2608 } 2609 catch (final ASN1Exception ae) 2610 { 2611 Debug.debugException(ae); 2612 throw new LDAPException(ResultCode.DECODING_ERROR, 2613 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get( 2614 StaticUtils.getExceptionMessage(ae)), 2615 ae); 2616 } 2617 notComp = decode(notFilterElement); 2618 break; 2619 2620 2621 2622 case FILTER_TYPE_EQUALITY: 2623 case FILTER_TYPE_GREATER_OR_EQUAL: 2624 case FILTER_TYPE_LESS_OR_EQUAL: 2625 case FILTER_TYPE_APPROXIMATE_MATCH: 2626 filterComps = NO_FILTERS; 2627 notComp = null; 2628 subInitial = null; 2629 subAny = NO_SUB_ANY; 2630 subFinal = null; 2631 matchingRuleID = null; 2632 dnAttributes = false; 2633 2634 final ASN1Sequence avaSequence; 2635 try 2636 { 2637 avaSequence = ASN1Sequence.decodeAsSequence(filterElement); 2638 } 2639 catch (final ASN1Exception ae) 2640 { 2641 Debug.debugException(ae); 2642 throw new LDAPException(ResultCode.DECODING_ERROR, 2643 ERR_FILTER_CANNOT_DECODE_AVA.get( 2644 StaticUtils.getExceptionMessage(ae)), 2645 ae); 2646 } 2647 2648 final ASN1Element[] avaElements = avaSequence.elements(); 2649 if (avaElements.length != 2) 2650 { 2651 throw new LDAPException(ResultCode.DECODING_ERROR, 2652 ERR_FILTER_INVALID_AVA_ELEMENT_COUNT.get( 2653 avaElements.length)); 2654 } 2655 2656 attrName = 2657 ASN1OctetString.decodeAsOctetString(avaElements[0]).stringValue(); 2658 assertionValue = ASN1OctetString.decodeAsOctetString(avaElements[1]); 2659 break; 2660 2661 2662 case FILTER_TYPE_SUBSTRING: 2663 filterComps = NO_FILTERS; 2664 notComp = null; 2665 assertionValue = null; 2666 matchingRuleID = null; 2667 dnAttributes = false; 2668 2669 final ASN1Sequence subFilterSequence; 2670 try 2671 { 2672 subFilterSequence = ASN1Sequence.decodeAsSequence(filterElement); 2673 } 2674 catch (final ASN1Exception ae) 2675 { 2676 Debug.debugException(ae); 2677 throw new LDAPException(ResultCode.DECODING_ERROR, 2678 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get( 2679 StaticUtils.getExceptionMessage(ae)), 2680 ae); 2681 } 2682 2683 final ASN1Element[] subFilterElements = subFilterSequence.elements(); 2684 if (subFilterElements.length != 2) 2685 { 2686 throw new LDAPException(ResultCode.DECODING_ERROR, 2687 ERR_FILTER_INVALID_SUBSTR_ASSERTION_COUNT.get( 2688 subFilterElements.length)); 2689 } 2690 2691 attrName = ASN1OctetString.decodeAsOctetString( 2692 subFilterElements[0]).stringValue(); 2693 2694 final ASN1Sequence subSequence; 2695 try 2696 { 2697 subSequence = ASN1Sequence.decodeAsSequence(subFilterElements[1]); 2698 } 2699 catch (final ASN1Exception ae) 2700 { 2701 Debug.debugException(ae); 2702 throw new LDAPException(ResultCode.DECODING_ERROR, 2703 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get( 2704 StaticUtils.getExceptionMessage(ae)), 2705 ae); 2706 } 2707 2708 ASN1OctetString tempSubInitial = null; 2709 ASN1OctetString tempSubFinal = null; 2710 final ArrayList<ASN1OctetString> subAnyList = new ArrayList<>(1); 2711 2712 final ASN1Element[] subElements = subSequence.elements(); 2713 for (final ASN1Element subElement : subElements) 2714 { 2715 switch (subElement.getType()) 2716 { 2717 case SUBSTRING_TYPE_SUBINITIAL: 2718 if (tempSubInitial == null) 2719 { 2720 tempSubInitial = 2721 ASN1OctetString.decodeAsOctetString(subElement); 2722 } 2723 else 2724 { 2725 throw new LDAPException(ResultCode.DECODING_ERROR, 2726 ERR_FILTER_MULTIPLE_SUBINITIAL.get()); 2727 } 2728 break; 2729 2730 case SUBSTRING_TYPE_SUBANY: 2731 subAnyList.add(ASN1OctetString.decodeAsOctetString(subElement)); 2732 break; 2733 2734 case SUBSTRING_TYPE_SUBFINAL: 2735 if (tempSubFinal == null) 2736 { 2737 tempSubFinal = ASN1OctetString.decodeAsOctetString(subElement); 2738 } 2739 else 2740 { 2741 throw new LDAPException(ResultCode.DECODING_ERROR, 2742 ERR_FILTER_MULTIPLE_SUBFINAL.get()); 2743 } 2744 break; 2745 2746 default: 2747 throw new LDAPException(ResultCode.DECODING_ERROR, 2748 ERR_FILTER_INVALID_SUBSTR_TYPE.get( 2749 StaticUtils.toHex(subElement.getType()))); 2750 } 2751 } 2752 2753 subInitial = tempSubInitial; 2754 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 2755 subFinal = tempSubFinal; 2756 break; 2757 2758 2759 case FILTER_TYPE_PRESENCE: 2760 filterComps = NO_FILTERS; 2761 notComp = null; 2762 assertionValue = null; 2763 subInitial = null; 2764 subAny = NO_SUB_ANY; 2765 subFinal = null; 2766 matchingRuleID = null; 2767 dnAttributes = false; 2768 attrName = 2769 ASN1OctetString.decodeAsOctetString(filterElement).stringValue(); 2770 break; 2771 2772 2773 case FILTER_TYPE_EXTENSIBLE_MATCH: 2774 filterComps = NO_FILTERS; 2775 notComp = null; 2776 subInitial = null; 2777 subAny = NO_SUB_ANY; 2778 subFinal = null; 2779 2780 final ASN1Sequence emSequence; 2781 try 2782 { 2783 emSequence = ASN1Sequence.decodeAsSequence(filterElement); 2784 } 2785 catch (final ASN1Exception ae) 2786 { 2787 Debug.debugException(ae); 2788 throw new LDAPException(ResultCode.DECODING_ERROR, 2789 ERR_FILTER_CANNOT_DECODE_EXTMATCH.get( 2790 StaticUtils.getExceptionMessage(ae)), 2791 ae); 2792 } 2793 2794 String tempAttrName = null; 2795 ASN1OctetString tempAssertionValue = null; 2796 String tempMatchingRuleID = null; 2797 boolean tempDNAttributes = false; 2798 for (final ASN1Element e : emSequence.elements()) 2799 { 2800 switch (e.getType()) 2801 { 2802 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 2803 if (tempAttrName == null) 2804 { 2805 tempAttrName = 2806 ASN1OctetString.decodeAsOctetString(e).stringValue(); 2807 } 2808 else 2809 { 2810 throw new LDAPException(ResultCode.DECODING_ERROR, 2811 ERR_FILTER_EXTMATCH_MULTIPLE_ATTRS.get()); 2812 } 2813 break; 2814 2815 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 2816 if (tempMatchingRuleID == null) 2817 { 2818 tempMatchingRuleID = 2819 ASN1OctetString.decodeAsOctetString(e).stringValue(); 2820 } 2821 else 2822 { 2823 throw new LDAPException(ResultCode.DECODING_ERROR, 2824 ERR_FILTER_EXTMATCH_MULTIPLE_MRIDS.get()); 2825 } 2826 break; 2827 2828 case EXTENSIBLE_TYPE_MATCH_VALUE: 2829 if (tempAssertionValue == null) 2830 { 2831 tempAssertionValue = ASN1OctetString.decodeAsOctetString(e); 2832 } 2833 else 2834 { 2835 throw new LDAPException(ResultCode.DECODING_ERROR, 2836 ERR_FILTER_EXTMATCH_MULTIPLE_VALUES.get()); 2837 } 2838 break; 2839 2840 case EXTENSIBLE_TYPE_DN_ATTRIBUTES: 2841 try 2842 { 2843 if (tempDNAttributes) 2844 { 2845 throw new LDAPException(ResultCode.DECODING_ERROR, 2846 ERR_FILTER_EXTMATCH_MULTIPLE_DNATTRS.get()); 2847 } 2848 else 2849 { 2850 tempDNAttributes = 2851 ASN1Boolean.decodeAsBoolean(e).booleanValue(); 2852 } 2853 } 2854 catch (final ASN1Exception ae) 2855 { 2856 Debug.debugException(ae); 2857 throw new LDAPException(ResultCode.DECODING_ERROR, 2858 ERR_FILTER_EXTMATCH_DNATTRS_NOT_BOOLEAN.get( 2859 StaticUtils.getExceptionMessage(ae)), 2860 ae); 2861 } 2862 break; 2863 2864 default: 2865 throw new LDAPException(ResultCode.DECODING_ERROR, 2866 ERR_FILTER_EXTMATCH_INVALID_TYPE.get( 2867 StaticUtils.toHex(e.getType()))); 2868 } 2869 } 2870 2871 if ((tempAttrName == null) && (tempMatchingRuleID == null)) 2872 { 2873 throw new LDAPException(ResultCode.DECODING_ERROR, 2874 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get()); 2875 } 2876 2877 if (tempAssertionValue == null) 2878 { 2879 throw new LDAPException(ResultCode.DECODING_ERROR, 2880 ERR_FILTER_EXTMATCH_NO_VALUE.get()); 2881 } 2882 2883 attrName = tempAttrName; 2884 assertionValue = tempAssertionValue; 2885 matchingRuleID = tempMatchingRuleID; 2886 dnAttributes = tempDNAttributes; 2887 break; 2888 2889 2890 default: 2891 throw new LDAPException(ResultCode.DECODING_ERROR, 2892 ERR_FILTER_ELEMENT_INVALID_TYPE.get( 2893 StaticUtils.toHex(filterElement.getType()))); 2894 } 2895 2896 2897 return new Filter(null, filterType, filterComps, notComp, attrName, 2898 assertionValue, subInitial, subAny, subFinal, 2899 matchingRuleID, dnAttributes); 2900 } 2901 2902 2903 2904 /** 2905 * Retrieves the filter type for this filter. 2906 * 2907 * @return The filter type for this filter. 2908 */ 2909 public byte getFilterType() 2910 { 2911 return filterType; 2912 } 2913 2914 2915 2916 /** 2917 * Retrieves the set of filter components used in this AND or OR filter. This 2918 * is not applicable for any other filter type. 2919 * 2920 * @return The set of filter components used in this AND or OR filter, or an 2921 * empty array if this is some other type of filter or if there are 2922 * no components (i.e., as in an LDAP TRUE or LDAP FALSE filter). 2923 */ 2924 public Filter[] getComponents() 2925 { 2926 return filterComps; 2927 } 2928 2929 2930 2931 /** 2932 * Retrieves the filter component used in this NOT filter. This is not 2933 * applicable for any other filter type. 2934 * 2935 * @return The filter component used in this NOT filter, or {@code null} if 2936 * this is some other type of filter. 2937 */ 2938 public Filter getNOTComponent() 2939 { 2940 return notComp; 2941 } 2942 2943 2944 2945 /** 2946 * Retrieves the name of the attribute type for this search filter. This is 2947 * applicable for the following types of filters: 2948 * <UL> 2949 * <LI>Equality</LI> 2950 * <LI>Substring</LI> 2951 * <LI>Greater or Equal</LI> 2952 * <LI>Less or Equal</LI> 2953 * <LI>Presence</LI> 2954 * <LI>Approximate Match</LI> 2955 * <LI>Extensible Match</LI> 2956 * </UL> 2957 * 2958 * @return The name of the attribute type for this search filter, or 2959 * {@code null} if it is not applicable for this type of filter. 2960 */ 2961 public String getAttributeName() 2962 { 2963 return attrName; 2964 } 2965 2966 2967 2968 /** 2969 * Retrieves the string representation of the assertion value for this search 2970 * filter. This is applicable for the following types of filters: 2971 * <UL> 2972 * <LI>Equality</LI> 2973 * <LI>Greater or Equal</LI> 2974 * <LI>Less or Equal</LI> 2975 * <LI>Approximate Match</LI> 2976 * <LI>Extensible Match</LI> 2977 * </UL> 2978 * 2979 * @return The string representation of the assertion value for this search 2980 * filter, or {@code null} if it is not applicable for this type of 2981 * filter. 2982 */ 2983 public String getAssertionValue() 2984 { 2985 if (assertionValue == null) 2986 { 2987 return null; 2988 } 2989 else 2990 { 2991 return assertionValue.stringValue(); 2992 } 2993 } 2994 2995 2996 2997 /** 2998 * Retrieves the binary representation of the assertion value for this search 2999 * filter. This is applicable for the following types of filters: 3000 * <UL> 3001 * <LI>Equality</LI> 3002 * <LI>Greater or Equal</LI> 3003 * <LI>Less or Equal</LI> 3004 * <LI>Approximate Match</LI> 3005 * <LI>Extensible Match</LI> 3006 * </UL> 3007 * 3008 * @return The binary representation of the assertion value for this search 3009 * filter, or {@code null} if it is not applicable for this type of 3010 * filter. 3011 */ 3012 public byte[] getAssertionValueBytes() 3013 { 3014 if (assertionValue == null) 3015 { 3016 return null; 3017 } 3018 else 3019 { 3020 return assertionValue.getValue(); 3021 } 3022 } 3023 3024 3025 3026 /** 3027 * Retrieves the raw assertion value for this search filter as an ASN.1 3028 * octet string. This is applicable for the following types of filters: 3029 * <UL> 3030 * <LI>Equality</LI> 3031 * <LI>Greater or Equal</LI> 3032 * <LI>Less or Equal</LI> 3033 * <LI>Approximate Match</LI> 3034 * <LI>Extensible Match</LI> 3035 * </UL> 3036 * 3037 * @return The raw assertion value for this search filter as an ASN.1 octet 3038 * string, or {@code null} if it is not applicable for this type of 3039 * filter. 3040 */ 3041 public ASN1OctetString getRawAssertionValue() 3042 { 3043 return assertionValue; 3044 } 3045 3046 3047 3048 /** 3049 * Retrieves the string representation of the subInitial element for this 3050 * substring filter. This is not applicable for any other filter type. 3051 * 3052 * @return The string representation of the subInitial 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 subInitial element. 3055 */ 3056 public String getSubInitialString() 3057 { 3058 if (subInitial == null) 3059 { 3060 return null; 3061 } 3062 else 3063 { 3064 return subInitial.stringValue(); 3065 } 3066 } 3067 3068 3069 3070 /** 3071 * Retrieves the binary representation of the subInitial element for this 3072 * substring filter. This is not applicable for any other filter type. 3073 * 3074 * @return The binary representation of the subInitial element for this 3075 * substring filter, or {@code null} if this is some other type of 3076 * filter, or if it is a substring filter with no subInitial element. 3077 */ 3078 public byte[] getSubInitialBytes() 3079 { 3080 if (subInitial == null) 3081 { 3082 return null; 3083 } 3084 else 3085 { 3086 return subInitial.getValue(); 3087 } 3088 } 3089 3090 3091 3092 /** 3093 * Retrieves the raw subInitial element for this filter as an ASN.1 octet 3094 * string. This is not applicable for any other filter type. 3095 * 3096 * @return The raw subInitial element for this filter as an ASN.1 octet 3097 * string, or {@code null} if this is not a substring filter, or if 3098 * it is a substring filter with no subInitial element. 3099 */ 3100 public ASN1OctetString getRawSubInitialValue() 3101 { 3102 return subInitial; 3103 } 3104 3105 3106 3107 /** 3108 * Retrieves the string representations of the subAny elements for this 3109 * substring filter. This is not applicable for any other filter type. 3110 * 3111 * @return The string representations of the subAny elements for this 3112 * substring filter, or an empty array if this is some other type of 3113 * filter, or if it is a substring filter with no subFinal element. 3114 */ 3115 public String[] getSubAnyStrings() 3116 { 3117 final String[] subAnyStrings = new String[subAny.length]; 3118 for (int i=0; i < subAny.length; i++) 3119 { 3120 subAnyStrings[i] = subAny[i].stringValue(); 3121 } 3122 3123 return subAnyStrings; 3124 } 3125 3126 3127 3128 /** 3129 * Retrieves the binary representations of the subAny elements for this 3130 * substring filter. This is not applicable for any other filter type. 3131 * 3132 * @return The binary representations of the subAny elements for this 3133 * substring filter, or an empty array if this is some other type of 3134 * filter, or if it is a substring filter with no subFinal element. 3135 */ 3136 public byte[][] getSubAnyBytes() 3137 { 3138 final byte[][] subAnyBytes = new byte[subAny.length][]; 3139 for (int i=0; i < subAny.length; i++) 3140 { 3141 subAnyBytes[i] = subAny[i].getValue(); 3142 } 3143 3144 return subAnyBytes; 3145 } 3146 3147 3148 3149 /** 3150 * Retrieves the raw subAny values for this substring filter. This is not 3151 * applicable for any other filter type. 3152 * 3153 * @return The raw subAny values for this substring filter, or an empty array 3154 * if this is some other type of filter, or if it is a substring 3155 * filter with no subFinal element. 3156 */ 3157 public ASN1OctetString[] getRawSubAnyValues() 3158 { 3159 return subAny; 3160 } 3161 3162 3163 3164 /** 3165 * Retrieves the string representation of the subFinal element for this 3166 * substring filter. This is not applicable for any other filter type. 3167 * 3168 * @return The string representation of the subFinal element for this 3169 * substring filter, or {@code null} if this is some other type of 3170 * filter, or if it is a substring filter with no subFinal element. 3171 */ 3172 public String getSubFinalString() 3173 { 3174 if (subFinal == null) 3175 { 3176 return null; 3177 } 3178 else 3179 { 3180 return subFinal.stringValue(); 3181 } 3182 } 3183 3184 3185 3186 /** 3187 * Retrieves the binary representation of the subFinal element for this 3188 * substring filter. This is not applicable for any other filter type. 3189 * 3190 * @return The binary representation of the subFinal element for this 3191 * substring filter, or {@code null} if this is some other type of 3192 * filter, or if it is a substring filter with no subFinal element. 3193 */ 3194 public byte[] getSubFinalBytes() 3195 { 3196 if (subFinal == null) 3197 { 3198 return null; 3199 } 3200 else 3201 { 3202 return subFinal.getValue(); 3203 } 3204 } 3205 3206 3207 3208 /** 3209 * Retrieves the raw subFinal element for this filter as an ASN.1 octet 3210 * string. This is not applicable for any other filter type. 3211 * 3212 * @return The raw subFinal element for this filter as an ASN.1 octet 3213 * string, or {@code null} if this is not a substring filter, or if 3214 * it is a substring filter with no subFinal element. 3215 */ 3216 public ASN1OctetString getRawSubFinalValue() 3217 { 3218 return subFinal; 3219 } 3220 3221 3222 3223 /** 3224 * Retrieves the matching rule ID for this extensible match filter. This is 3225 * not applicable for any other filter type. 3226 * 3227 * @return The matching rule ID for this extensible match filter, or 3228 * {@code null} if this is some other type of filter, or if this 3229 * extensible match filter does not have a matching rule ID. 3230 */ 3231 public String getMatchingRuleID() 3232 { 3233 return matchingRuleID; 3234 } 3235 3236 3237 3238 /** 3239 * Retrieves the dnAttributes flag for this extensible match filter. This is 3240 * not applicable for any other filter type. 3241 * 3242 * @return The dnAttributes flag for this extensible match filter. 3243 */ 3244 public boolean getDNAttributes() 3245 { 3246 return dnAttributes; 3247 } 3248 3249 3250 3251 /** 3252 * Indicates whether this filter matches the provided entry. Note that this 3253 * is a best-guess effort and may not be completely accurate in all cases. 3254 * All matching will be performed using case-ignore string matching, which may 3255 * yield an unexpected result for values that should not be treated as simple 3256 * strings. For example: 3257 * <UL> 3258 * <LI>Two DN values which are logically equivalent may not be considered 3259 * matches if they have different spacing.</LI> 3260 * <LI>Ordering comparisons against numeric values may yield unexpected 3261 * results (e.g., "2" will be considered greater than "10" because the 3262 * character "2" has a larger ASCII value than the character "1").</LI> 3263 * </UL> 3264 * <BR> 3265 * In addition to the above constraints, it should be noted that neither 3266 * approximate matching nor extensible matching are currently supported. 3267 * 3268 * @param entry The entry for which to make the determination. It must not 3269 * be {@code null}. 3270 * 3271 * @return {@code true} if this filter appears to match the provided entry, 3272 * or {@code false} if not. 3273 * 3274 * @throws LDAPException If a problem occurs while trying to make the 3275 * determination. 3276 */ 3277 public boolean matchesEntry(final Entry entry) 3278 throws LDAPException 3279 { 3280 return matchesEntry(entry, entry.getSchema()); 3281 } 3282 3283 3284 3285 /** 3286 * Indicates whether this filter matches the provided entry. Note that this 3287 * is a best-guess effort and may not be completely accurate in all cases. 3288 * If provided, the given schema will be used in an attempt to determine the 3289 * appropriate matching rule for making the determinations, but some corner 3290 * cases may not be handled accurately. Neither approximate matching nor 3291 * extensible matching are currently supported. 3292 * 3293 * @param entry The entry for which to make the determination. It must not 3294 * be {@code null}. 3295 * @param schema The schema to use when making the determination. If this 3296 * is {@code null}, then all matching will be performed using 3297 * a case-ignore matching rule. 3298 * 3299 * @return {@code true} if this filter appears to match the provided entry, 3300 * or {@code false} if not. 3301 * 3302 * @throws LDAPException If a problem occurs while trying to make the 3303 * determination. 3304 */ 3305 public boolean matchesEntry(final Entry entry, final Schema schema) 3306 throws LDAPException 3307 { 3308 Validator.ensureNotNull(entry); 3309 3310 switch (filterType) 3311 { 3312 case FILTER_TYPE_AND: 3313 for (final Filter f : filterComps) 3314 { 3315 if (! f.matchesEntry(entry, schema)) 3316 { 3317 return false; 3318 } 3319 } 3320 return true; 3321 3322 case FILTER_TYPE_OR: 3323 for (final Filter f : filterComps) 3324 { 3325 if (f.matchesEntry(entry, schema)) 3326 { 3327 return true; 3328 } 3329 } 3330 return false; 3331 3332 case FILTER_TYPE_NOT: 3333 return (! notComp.matchesEntry(entry, schema)); 3334 3335 case FILTER_TYPE_EQUALITY: 3336 Attribute a = entry.getAttribute(attrName, schema); 3337 if (a == null) 3338 { 3339 return false; 3340 } 3341 3342 MatchingRule matchingRule = 3343 MatchingRule.selectEqualityMatchingRule(attrName, schema); 3344 return matchingRule.matchesAnyValue(assertionValue, a.getRawValues()); 3345 3346 case FILTER_TYPE_SUBSTRING: 3347 a = entry.getAttribute(attrName, schema); 3348 if (a == null) 3349 { 3350 return false; 3351 } 3352 3353 matchingRule = 3354 MatchingRule.selectSubstringMatchingRule(attrName, schema); 3355 for (final ASN1OctetString v : a.getRawValues()) 3356 { 3357 if (matchingRule.matchesSubstring(v, subInitial, subAny, subFinal)) 3358 { 3359 return true; 3360 } 3361 } 3362 return false; 3363 3364 case FILTER_TYPE_GREATER_OR_EQUAL: 3365 a = entry.getAttribute(attrName, schema); 3366 if (a == null) 3367 { 3368 return false; 3369 } 3370 3371 matchingRule = 3372 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3373 for (final ASN1OctetString v : a.getRawValues()) 3374 { 3375 if (matchingRule.compareValues(v, assertionValue) >= 0) 3376 { 3377 return true; 3378 } 3379 } 3380 return false; 3381 3382 case FILTER_TYPE_LESS_OR_EQUAL: 3383 a = entry.getAttribute(attrName, schema); 3384 if (a == null) 3385 { 3386 return false; 3387 } 3388 3389 matchingRule = 3390 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3391 for (final ASN1OctetString v : a.getRawValues()) 3392 { 3393 if (matchingRule.compareValues(v, assertionValue) <= 0) 3394 { 3395 return true; 3396 } 3397 } 3398 return false; 3399 3400 case FILTER_TYPE_PRESENCE: 3401 return (entry.hasAttribute(attrName)); 3402 3403 case FILTER_TYPE_APPROXIMATE_MATCH: 3404 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3405 ERR_FILTER_APPROXIMATE_MATCHING_NOT_SUPPORTED.get()); 3406 3407 case FILTER_TYPE_EXTENSIBLE_MATCH: 3408 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3409 ERR_FILTER_EXTENSIBLE_MATCHING_NOT_SUPPORTED.get()); 3410 3411 default: 3412 throw new LDAPException(ResultCode.PARAM_ERROR, 3413 ERR_FILTER_INVALID_TYPE.get()); 3414 } 3415 } 3416 3417 3418 3419 /** 3420 * Attempts to simplify the provided filter to allow it to be more efficiently 3421 * processed by the server. The simplifications it will make include: 3422 * <UL> 3423 * <LI>Any AND or OR filter that contains only a single filter component 3424 * will be converted to just that embedded filter component to eliminate 3425 * the unnecessary AND or OR wrapper. For example, the filter 3426 * "(&(uid=john.doe))" will be converted to just 3427 * "(uid=john.doe)".</LI> 3428 * <LI>Any AND components inside of an AND filter will be merged into the 3429 * outer AND filter. Any OR components inside of an OR filter will be 3430 * merged into the outer OR filter. For example, the filter 3431 * "(&(objectClass=person)(&(givenName=John)(sn=Doe)))" will be 3432 * converted to 3433 * "(&(objectClass=person)(givenName=John)(sn=Doe))".</LI> 3434 * <LI>If {@code reOrderElements} is true, then this method will attempt to 3435 * re-order the elements inside AND and OR filters in an attempt to 3436 * ensure that the components which are likely to be the most efficient 3437 * come earlier than those which are likely to be the least efficient. 3438 * This can speed up processing in servers that process filter 3439 * components in a left-to-right order.</LI> 3440 * </UL> 3441 * <BR><BR> 3442 * The simplification will happen recursively, in an attempt to generate a 3443 * filter that is as simple and efficient as possible. 3444 * 3445 * @param filter The filter to attempt to simplify. 3446 * @param reOrderElements Indicates whether this method may re-order the 3447 * elements in the filter so that, in a server that 3448 * evaluates the components in a left-to-right order, 3449 * the components which are likely to be more 3450 * efficient to process will be listed before those 3451 * which are likely to be less efficient. 3452 * 3453 * @return The simplified filter, or the original filter if the provided 3454 * filter is not one that can be simplified any further. 3455 */ 3456 public static Filter simplifyFilter(final Filter filter, 3457 final boolean reOrderElements) 3458 { 3459 final byte filterType = filter.filterType; 3460 switch (filterType) 3461 { 3462 case FILTER_TYPE_AND: 3463 case FILTER_TYPE_OR: 3464 // These will be handled below. 3465 break; 3466 3467 case FILTER_TYPE_NOT: 3468 // We may be able to simplify the filter component contained inside the 3469 // NOT. 3470 return createNOTFilter(simplifyFilter(filter.notComp, reOrderElements)); 3471 3472 default: 3473 // We can't simplify this filter, so just return what was provided. 3474 return filter; 3475 } 3476 3477 3478 // An AND filter with zero components is an LDAP true filter, and we can't 3479 // simplify that. An OR filter with zero components is an LDAP false 3480 // filter, and we can't simplify that either. The set of components 3481 // should never be null for an AND or OR filter, but if that happens to be 3482 // the case, then we'll return the original filter. 3483 final Filter[] components = filter.filterComps; 3484 if ((components == null) || (components.length == 0)) 3485 { 3486 return filter; 3487 } 3488 3489 3490 // For either an AND or an OR filter with just a single component, then just 3491 // return that embedded component. But simplify it first. 3492 if (components.length == 1) 3493 { 3494 return simplifyFilter(components[0], reOrderElements); 3495 } 3496 3497 3498 // If we've gotten here, then we have a filter with multiple components. 3499 // Simplify each of them to the extent possible, un-embed any ANDs 3500 // contained inside an AND or ORs contained inside an OR, and eliminate any 3501 // duplicate components in the resulting top-level filter. 3502 final LinkedHashSet<Filter> componentSet = new LinkedHashSet<>(10); 3503 for (final Filter f : components) 3504 { 3505 final Filter simplifiedFilter = simplifyFilter(f, reOrderElements); 3506 if (simplifiedFilter.filterType == FILTER_TYPE_AND) 3507 { 3508 if (filterType == FILTER_TYPE_AND) 3509 { 3510 // This is an AND nested inside an AND. In that case, we'll just put 3511 // all the nested components inside the outer AND. 3512 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3513 } 3514 else 3515 { 3516 componentSet.add(simplifiedFilter); 3517 } 3518 } 3519 else if (simplifiedFilter.filterType == FILTER_TYPE_OR) 3520 { 3521 if (filterType == FILTER_TYPE_OR) 3522 { 3523 // This is an OR nested inside an OR. In that case, we'll just put 3524 // all the nested components inside the outer OR. 3525 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3526 } 3527 else 3528 { 3529 componentSet.add(simplifiedFilter); 3530 } 3531 } 3532 else 3533 { 3534 componentSet.add(simplifiedFilter); 3535 } 3536 } 3537 3538 3539 // It's possible at this point that we are down to just a single component. 3540 // That can happen if the filter was an AND or an OR with a duplicate 3541 // element, like "(&(a=b)(a=b))". In that case, just return that one 3542 // component. 3543 if (componentSet.size() == 1) 3544 { 3545 return componentSet.iterator().next(); 3546 } 3547 3548 3549 // If we should re-order the components, then use the following priority 3550 // list: 3551 // 3552 // 1. Equality components that target an attribute other than objectClass. 3553 // These are most likely to require only a single database lookup to get 3554 // the candidate list, and that candidate list will frequently be small. 3555 // 2. Equality components that target the objectClass attribute. These are 3556 // likely to require only a single database lookup to get the candidate 3557 // list, but the candidate list is more likely to be larger. 3558 // 3. Approximate match components. These are also likely to require only 3559 // a single database lookup to get the candidate list, but that 3560 // candidate list is likely to have a larger number of candidates. 3561 // 4. Presence components that target an attribute other than objectClass. 3562 // These are also likely to require only a single database lookup to get 3563 // the candidate list, but are likely to have a large number of 3564 // candidates. 3565 // 5. Substring components that have a subInitial element. These are 3566 // generally the most efficient substring filters to process, requiring 3567 // access to fewer database keys than substring filters with only subAny 3568 // and/or subFinal components. 3569 // 6. Substring components that only have subAny and/or subFinal elements. 3570 // These will probably require a number of database lookups and will 3571 // probably result in large candidate lists. 3572 // 7. Greater-or-equal components and less-or-equal components. These 3573 // will probably require a number of database lookups and will probably 3574 // result in large candidate lists. 3575 // 8. Extensible match components. Even if these are indexed, there isn't 3576 // any good way to know how expensive they might be to process or how 3577 // big the candidate list might be. 3578 // 9. Presence components that target the objectClass attribute. This is 3579 // likely to require only a single database lookup to get the candidate 3580 // list, but the candidate list will also be extremely large (if it's 3581 // indexed at all) since it will match every entry. 3582 // 10. NOT components. These are generally not possible to index and 3583 // therefore cannot be used to create a candidate list. 3584 // 3585 // AND and OR components will be ordered according to the first of their 3586 // embedded components Since the filter has already been simplified, then 3587 // the first element in the list will be the one we think will be the most 3588 // efficient to process. 3589 if (reOrderElements) 3590 { 3591 final TreeMap<Integer,LinkedHashSet<Filter>> m = new TreeMap<>(); 3592 for (final Filter f : componentSet) 3593 { 3594 final Filter prioritizeComp; 3595 if ((f.filterType == FILTER_TYPE_AND) || 3596 (f.filterType == FILTER_TYPE_OR)) 3597 { 3598 if (f.filterComps.length > 0) 3599 { 3600 prioritizeComp = f.filterComps[0]; 3601 } 3602 else 3603 { 3604 prioritizeComp = f; 3605 } 3606 } 3607 else 3608 { 3609 prioritizeComp = f; 3610 } 3611 3612 final Integer slot; 3613 switch (prioritizeComp.filterType) 3614 { 3615 case FILTER_TYPE_EQUALITY: 3616 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3617 { 3618 slot = 2; 3619 } 3620 else 3621 { 3622 slot = 1; 3623 } 3624 break; 3625 3626 case FILTER_TYPE_APPROXIMATE_MATCH: 3627 slot = 3; 3628 break; 3629 3630 case FILTER_TYPE_PRESENCE: 3631 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3632 { 3633 slot = 9; 3634 } 3635 else 3636 { 3637 slot = 4; 3638 } 3639 break; 3640 3641 case FILTER_TYPE_SUBSTRING: 3642 if (prioritizeComp.subInitial == null) 3643 { 3644 slot = 6; 3645 } 3646 else 3647 { 3648 slot = 5; 3649 } 3650 break; 3651 3652 case FILTER_TYPE_GREATER_OR_EQUAL: 3653 case FILTER_TYPE_LESS_OR_EQUAL: 3654 slot = 7; 3655 break; 3656 3657 case FILTER_TYPE_EXTENSIBLE_MATCH: 3658 slot = 8; 3659 break; 3660 3661 case FILTER_TYPE_NOT: 3662 default: 3663 slot = 10; 3664 break; 3665 } 3666 3667 LinkedHashSet<Filter> filterSet = m.get(slot-1); 3668 if (filterSet == null) 3669 { 3670 filterSet = new LinkedHashSet<>(10); 3671 m.put(slot-1, filterSet); 3672 } 3673 filterSet.add(f); 3674 } 3675 3676 componentSet.clear(); 3677 for (final LinkedHashSet<Filter> filterSet : m.values()) 3678 { 3679 componentSet.addAll(filterSet); 3680 } 3681 } 3682 3683 3684 // Return the new, possibly simplified filter. 3685 if (filterType == FILTER_TYPE_AND) 3686 { 3687 return createANDFilter(componentSet); 3688 } 3689 else 3690 { 3691 return createORFilter(componentSet); 3692 } 3693 } 3694 3695 3696 3697 /** 3698 * Generates a hash code for this search filter. 3699 * 3700 * @return The generated hash code for this search filter. 3701 */ 3702 @Override() 3703 public int hashCode() 3704 { 3705 final CaseIgnoreStringMatchingRule matchingRule = 3706 CaseIgnoreStringMatchingRule.getInstance(); 3707 int hashCode = filterType; 3708 3709 switch (filterType) 3710 { 3711 case FILTER_TYPE_AND: 3712 case FILTER_TYPE_OR: 3713 for (final Filter f : filterComps) 3714 { 3715 hashCode += f.hashCode(); 3716 } 3717 break; 3718 3719 case FILTER_TYPE_NOT: 3720 hashCode += notComp.hashCode(); 3721 break; 3722 3723 case FILTER_TYPE_EQUALITY: 3724 case FILTER_TYPE_GREATER_OR_EQUAL: 3725 case FILTER_TYPE_LESS_OR_EQUAL: 3726 case FILTER_TYPE_APPROXIMATE_MATCH: 3727 hashCode += StaticUtils.toLowerCase(attrName).hashCode(); 3728 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3729 break; 3730 3731 case FILTER_TYPE_SUBSTRING: 3732 hashCode += StaticUtils.toLowerCase(attrName).hashCode(); 3733 if (subInitial != null) 3734 { 3735 hashCode += matchingRule.normalizeSubstring(subInitial, 3736 MatchingRule.SUBSTRING_TYPE_SUBINITIAL).hashCode(); 3737 } 3738 for (final ASN1OctetString s : subAny) 3739 { 3740 hashCode += matchingRule.normalizeSubstring(s, 3741 MatchingRule.SUBSTRING_TYPE_SUBANY).hashCode(); 3742 } 3743 if (subFinal != null) 3744 { 3745 hashCode += matchingRule.normalizeSubstring(subFinal, 3746 MatchingRule.SUBSTRING_TYPE_SUBFINAL).hashCode(); 3747 } 3748 break; 3749 3750 case FILTER_TYPE_PRESENCE: 3751 hashCode += StaticUtils.toLowerCase(attrName).hashCode(); 3752 break; 3753 3754 case FILTER_TYPE_EXTENSIBLE_MATCH: 3755 if (attrName != null) 3756 { 3757 hashCode += StaticUtils.toLowerCase(attrName).hashCode(); 3758 } 3759 3760 if (matchingRuleID != null) 3761 { 3762 hashCode += StaticUtils.toLowerCase(matchingRuleID).hashCode(); 3763 } 3764 3765 if (dnAttributes) 3766 { 3767 hashCode++; 3768 } 3769 3770 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3771 break; 3772 } 3773 3774 return hashCode; 3775 } 3776 3777 3778 3779 /** 3780 * Indicates whether the provided object is equal to this search filter. 3781 * 3782 * @param o The object for which to make the determination. 3783 * 3784 * @return {@code true} if the provided object can be considered equal to 3785 * this search filter, or {@code false} if not. 3786 */ 3787 @Override() 3788 public boolean equals(final Object o) 3789 { 3790 if (o == null) 3791 { 3792 return false; 3793 } 3794 3795 if (o == this) 3796 { 3797 return true; 3798 } 3799 3800 if (! (o instanceof Filter)) 3801 { 3802 return false; 3803 } 3804 3805 final Filter f = (Filter) o; 3806 if (filterType != f.filterType) 3807 { 3808 return false; 3809 } 3810 3811 final CaseIgnoreStringMatchingRule matchingRule = 3812 CaseIgnoreStringMatchingRule.getInstance(); 3813 3814 switch (filterType) 3815 { 3816 case FILTER_TYPE_AND: 3817 case FILTER_TYPE_OR: 3818 if (filterComps.length != f.filterComps.length) 3819 { 3820 return false; 3821 } 3822 3823 final HashSet<Filter> compSet = new HashSet<>(10); 3824 compSet.addAll(Arrays.asList(filterComps)); 3825 3826 for (final Filter filterComp : f.filterComps) 3827 { 3828 if (! compSet.remove(filterComp)) 3829 { 3830 return false; 3831 } 3832 } 3833 3834 return true; 3835 3836 3837 case FILTER_TYPE_NOT: 3838 return notComp.equals(f.notComp); 3839 3840 3841 case FILTER_TYPE_EQUALITY: 3842 case FILTER_TYPE_GREATER_OR_EQUAL: 3843 case FILTER_TYPE_LESS_OR_EQUAL: 3844 case FILTER_TYPE_APPROXIMATE_MATCH: 3845 return (attrName.equalsIgnoreCase(f.attrName) && 3846 matchingRule.valuesMatch(assertionValue, f.assertionValue)); 3847 3848 3849 case FILTER_TYPE_SUBSTRING: 3850 if (! attrName.equalsIgnoreCase(f.attrName)) 3851 { 3852 return false; 3853 } 3854 3855 if (subAny.length != f.subAny.length) 3856 { 3857 return false; 3858 } 3859 3860 if (subInitial == null) 3861 { 3862 if (f.subInitial != null) 3863 { 3864 return false; 3865 } 3866 } 3867 else 3868 { 3869 if (f.subInitial == null) 3870 { 3871 return false; 3872 } 3873 3874 final ASN1OctetString si1 = matchingRule.normalizeSubstring( 3875 subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3876 final ASN1OctetString si2 = matchingRule.normalizeSubstring( 3877 f.subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3878 if (! si1.equals(si2)) 3879 { 3880 return false; 3881 } 3882 } 3883 3884 for (int i=0; i < subAny.length; i++) 3885 { 3886 final ASN1OctetString sa1 = matchingRule.normalizeSubstring(subAny[i], 3887 MatchingRule.SUBSTRING_TYPE_SUBANY); 3888 final ASN1OctetString sa2 = matchingRule.normalizeSubstring( 3889 f.subAny[i], MatchingRule.SUBSTRING_TYPE_SUBANY); 3890 if (! sa1.equals(sa2)) 3891 { 3892 return false; 3893 } 3894 } 3895 3896 if (subFinal == null) 3897 { 3898 if (f.subFinal != null) 3899 { 3900 return false; 3901 } 3902 } 3903 else 3904 { 3905 if (f.subFinal == null) 3906 { 3907 return false; 3908 } 3909 3910 final ASN1OctetString sf1 = matchingRule.normalizeSubstring(subFinal, 3911 MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3912 final ASN1OctetString sf2 = matchingRule.normalizeSubstring( 3913 f.subFinal, MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3914 if (! sf1.equals(sf2)) 3915 { 3916 return false; 3917 } 3918 } 3919 3920 return true; 3921 3922 3923 case FILTER_TYPE_PRESENCE: 3924 return (attrName.equalsIgnoreCase(f.attrName)); 3925 3926 3927 case FILTER_TYPE_EXTENSIBLE_MATCH: 3928 if (attrName == null) 3929 { 3930 if (f.attrName != null) 3931 { 3932 return false; 3933 } 3934 } 3935 else 3936 { 3937 if (f.attrName == null) 3938 { 3939 return false; 3940 } 3941 else 3942 { 3943 if (! attrName.equalsIgnoreCase(f.attrName)) 3944 { 3945 return false; 3946 } 3947 } 3948 } 3949 3950 if (matchingRuleID == null) 3951 { 3952 if (f.matchingRuleID != null) 3953 { 3954 return false; 3955 } 3956 } 3957 else 3958 { 3959 if (f.matchingRuleID == null) 3960 { 3961 return false; 3962 } 3963 else 3964 { 3965 if (! matchingRuleID.equalsIgnoreCase(f.matchingRuleID)) 3966 { 3967 return false; 3968 } 3969 } 3970 } 3971 3972 if (dnAttributes != f.dnAttributes) 3973 { 3974 return false; 3975 } 3976 3977 return matchingRule.valuesMatch(assertionValue, f.assertionValue); 3978 3979 3980 default: 3981 return false; 3982 } 3983 } 3984 3985 3986 3987 /** 3988 * Retrieves a string representation of this search filter. 3989 * 3990 * @return A string representation of this search filter. 3991 */ 3992 @Override() 3993 public String toString() 3994 { 3995 if (filterString == null) 3996 { 3997 final StringBuilder buffer = new StringBuilder(); 3998 toString(buffer); 3999 filterString = buffer.toString(); 4000 } 4001 4002 return filterString; 4003 } 4004 4005 4006 4007 /** 4008 * Appends a string representation of this search filter to the provided 4009 * buffer. 4010 * 4011 * @param buffer The buffer to which to append a string representation of 4012 * this search filter. 4013 */ 4014 public void toString(final StringBuilder buffer) 4015 { 4016 switch (filterType) 4017 { 4018 case FILTER_TYPE_AND: 4019 buffer.append("(&"); 4020 for (final Filter f : filterComps) 4021 { 4022 f.toString(buffer); 4023 } 4024 buffer.append(')'); 4025 break; 4026 4027 case FILTER_TYPE_OR: 4028 buffer.append("(|"); 4029 for (final Filter f : filterComps) 4030 { 4031 f.toString(buffer); 4032 } 4033 buffer.append(')'); 4034 break; 4035 4036 case FILTER_TYPE_NOT: 4037 buffer.append("(!"); 4038 notComp.toString(buffer); 4039 buffer.append(')'); 4040 break; 4041 4042 case FILTER_TYPE_EQUALITY: 4043 buffer.append('('); 4044 buffer.append(attrName); 4045 buffer.append('='); 4046 encodeValue(assertionValue, buffer); 4047 buffer.append(')'); 4048 break; 4049 4050 case FILTER_TYPE_SUBSTRING: 4051 buffer.append('('); 4052 buffer.append(attrName); 4053 buffer.append('='); 4054 if (subInitial != null) 4055 { 4056 encodeValue(subInitial, buffer); 4057 } 4058 buffer.append('*'); 4059 for (final ASN1OctetString s : subAny) 4060 { 4061 encodeValue(s, buffer); 4062 buffer.append('*'); 4063 } 4064 if (subFinal != null) 4065 { 4066 encodeValue(subFinal, buffer); 4067 } 4068 buffer.append(')'); 4069 break; 4070 4071 case FILTER_TYPE_GREATER_OR_EQUAL: 4072 buffer.append('('); 4073 buffer.append(attrName); 4074 buffer.append(">="); 4075 encodeValue(assertionValue, buffer); 4076 buffer.append(')'); 4077 break; 4078 4079 case FILTER_TYPE_LESS_OR_EQUAL: 4080 buffer.append('('); 4081 buffer.append(attrName); 4082 buffer.append("<="); 4083 encodeValue(assertionValue, buffer); 4084 buffer.append(')'); 4085 break; 4086 4087 case FILTER_TYPE_PRESENCE: 4088 buffer.append('('); 4089 buffer.append(attrName); 4090 buffer.append("=*)"); 4091 break; 4092 4093 case FILTER_TYPE_APPROXIMATE_MATCH: 4094 buffer.append('('); 4095 buffer.append(attrName); 4096 buffer.append("~="); 4097 encodeValue(assertionValue, buffer); 4098 buffer.append(')'); 4099 break; 4100 4101 case FILTER_TYPE_EXTENSIBLE_MATCH: 4102 buffer.append('('); 4103 if (attrName != null) 4104 { 4105 buffer.append(attrName); 4106 } 4107 4108 if (dnAttributes) 4109 { 4110 buffer.append(":dn"); 4111 } 4112 4113 if (matchingRuleID != null) 4114 { 4115 buffer.append(':'); 4116 buffer.append(matchingRuleID); 4117 } 4118 4119 buffer.append(":="); 4120 encodeValue(assertionValue, buffer); 4121 buffer.append(')'); 4122 break; 4123 } 4124 } 4125 4126 4127 4128 /** 4129 * Retrieves a normalized string representation of this search filter. 4130 * 4131 * @return A normalized string representation of this search filter. 4132 */ 4133 public String toNormalizedString() 4134 { 4135 if (normalizedString == null) 4136 { 4137 final StringBuilder buffer = new StringBuilder(); 4138 toNormalizedString(buffer); 4139 normalizedString = buffer.toString(); 4140 } 4141 4142 return normalizedString; 4143 } 4144 4145 4146 4147 /** 4148 * Appends a normalized string representation of this search filter to the 4149 * provided buffer. 4150 * 4151 * @param buffer The buffer to which to append a normalized string 4152 * representation of this search filter. 4153 */ 4154 public void toNormalizedString(final StringBuilder buffer) 4155 { 4156 final CaseIgnoreStringMatchingRule mr = 4157 CaseIgnoreStringMatchingRule.getInstance(); 4158 4159 switch (filterType) 4160 { 4161 case FILTER_TYPE_AND: 4162 buffer.append("(&"); 4163 for (final Filter f : filterComps) 4164 { 4165 f.toNormalizedString(buffer); 4166 } 4167 buffer.append(')'); 4168 break; 4169 4170 case FILTER_TYPE_OR: 4171 buffer.append("(|"); 4172 for (final Filter f : filterComps) 4173 { 4174 f.toNormalizedString(buffer); 4175 } 4176 buffer.append(')'); 4177 break; 4178 4179 case FILTER_TYPE_NOT: 4180 buffer.append("(!"); 4181 notComp.toNormalizedString(buffer); 4182 buffer.append(')'); 4183 break; 4184 4185 case FILTER_TYPE_EQUALITY: 4186 buffer.append('('); 4187 buffer.append(StaticUtils.toLowerCase(attrName)); 4188 buffer.append('='); 4189 encodeValue(mr.normalize(assertionValue), buffer); 4190 buffer.append(')'); 4191 break; 4192 4193 case FILTER_TYPE_SUBSTRING: 4194 buffer.append('('); 4195 buffer.append(StaticUtils.toLowerCase(attrName)); 4196 buffer.append('='); 4197 if (subInitial != null) 4198 { 4199 encodeValue(mr.normalizeSubstring(subInitial, 4200 MatchingRule.SUBSTRING_TYPE_SUBINITIAL), buffer); 4201 } 4202 buffer.append('*'); 4203 for (final ASN1OctetString s : subAny) 4204 { 4205 encodeValue(mr.normalizeSubstring(s, 4206 MatchingRule.SUBSTRING_TYPE_SUBANY), buffer); 4207 buffer.append('*'); 4208 } 4209 if (subFinal != null) 4210 { 4211 encodeValue(mr.normalizeSubstring(subFinal, 4212 MatchingRule.SUBSTRING_TYPE_SUBFINAL), buffer); 4213 } 4214 buffer.append(')'); 4215 break; 4216 4217 case FILTER_TYPE_GREATER_OR_EQUAL: 4218 buffer.append('('); 4219 buffer.append(StaticUtils.toLowerCase(attrName)); 4220 buffer.append(">="); 4221 encodeValue(mr.normalize(assertionValue), buffer); 4222 buffer.append(')'); 4223 break; 4224 4225 case FILTER_TYPE_LESS_OR_EQUAL: 4226 buffer.append('('); 4227 buffer.append(StaticUtils.toLowerCase(attrName)); 4228 buffer.append("<="); 4229 encodeValue(mr.normalize(assertionValue), buffer); 4230 buffer.append(')'); 4231 break; 4232 4233 case FILTER_TYPE_PRESENCE: 4234 buffer.append('('); 4235 buffer.append(StaticUtils.toLowerCase(attrName)); 4236 buffer.append("=*)"); 4237 break; 4238 4239 case FILTER_TYPE_APPROXIMATE_MATCH: 4240 buffer.append('('); 4241 buffer.append(StaticUtils.toLowerCase(attrName)); 4242 buffer.append("~="); 4243 encodeValue(mr.normalize(assertionValue), buffer); 4244 buffer.append(')'); 4245 break; 4246 4247 case FILTER_TYPE_EXTENSIBLE_MATCH: 4248 buffer.append('('); 4249 if (attrName != null) 4250 { 4251 buffer.append(StaticUtils.toLowerCase(attrName)); 4252 } 4253 4254 if (dnAttributes) 4255 { 4256 buffer.append(":dn"); 4257 } 4258 4259 if (matchingRuleID != null) 4260 { 4261 buffer.append(':'); 4262 buffer.append(StaticUtils.toLowerCase(matchingRuleID)); 4263 } 4264 4265 buffer.append(":="); 4266 encodeValue(mr.normalize(assertionValue), buffer); 4267 buffer.append(')'); 4268 break; 4269 } 4270 } 4271 4272 4273 4274 /** 4275 * Encodes the provided value into a form suitable for use as the assertion 4276 * value in the string representation of a search filter. Parentheses, 4277 * asterisks, backslashes, null characters, and any non-ASCII characters will 4278 * be escaped using a backslash before the hexadecimal representation of each 4279 * byte in the character to escape. 4280 * 4281 * @param value The value to be encoded. It must not be {@code null}. 4282 * 4283 * @return The encoded representation of the provided string. 4284 */ 4285 public static String encodeValue(final String value) 4286 { 4287 Validator.ensureNotNull(value); 4288 4289 final StringBuilder buffer = new StringBuilder(); 4290 encodeValue(new ASN1OctetString(value), buffer); 4291 return buffer.toString(); 4292 } 4293 4294 4295 4296 /** 4297 * Encodes the provided value into a form suitable for use as the assertion 4298 * value in the string representation of a search filter. Parentheses, 4299 * asterisks, backslashes, null characters, and any non-ASCII characters will 4300 * be escaped using a backslash before the hexadecimal representation of each 4301 * byte in the character to escape. 4302 * 4303 * @param value The value to be encoded. It must not be {@code null}. 4304 * 4305 * @return The encoded representation of the provided string. 4306 */ 4307 public static String encodeValue(final byte[]value) 4308 { 4309 Validator.ensureNotNull(value); 4310 4311 final StringBuilder buffer = new StringBuilder(); 4312 encodeValue(new ASN1OctetString(value), buffer); 4313 return buffer.toString(); 4314 } 4315 4316 4317 4318 /** 4319 * Appends the assertion value for this filter to the provided buffer, 4320 * encoding any special characters as necessary. 4321 * 4322 * @param value The value to be encoded. 4323 * @param buffer The buffer to which the assertion value should be appended. 4324 */ 4325 public static void encodeValue(final ASN1OctetString value, 4326 final StringBuilder buffer) 4327 { 4328 final byte[] valueBytes = value.getValue(); 4329 for (int i=0; i < valueBytes.length; i++) 4330 { 4331 switch (StaticUtils.numBytesInUTF8CharacterWithFirstByte(valueBytes[i])) 4332 { 4333 case 1: 4334 // This character is ASCII, but might still need to be escaped. 4335 if ((valueBytes[i] <= 0x1F) || // Non-printable ASCII characters. 4336 (valueBytes[i] == 0x28) || // Open parenthesis 4337 (valueBytes[i] == 0x29) || // Close parenthesis 4338 (valueBytes[i] == 0x2A) || // Asterisk 4339 (valueBytes[i] == 0x5C) || // Backslash 4340 (valueBytes[i] == 0x7F)) // DEL 4341 { 4342 buffer.append('\\'); 4343 StaticUtils.toHex(valueBytes[i], buffer); 4344 } 4345 else 4346 { 4347 buffer.append((char) valueBytes[i]); 4348 } 4349 break; 4350 4351 case 2: 4352 // If there are at least two bytes left, then we'll hex-encode the 4353 // next two bytes. Otherwise we'll hex-encode whatever is left. 4354 buffer.append('\\'); 4355 StaticUtils.toHex(valueBytes[i++], buffer); 4356 if (i < valueBytes.length) 4357 { 4358 buffer.append('\\'); 4359 StaticUtils.toHex(valueBytes[i], buffer); 4360 } 4361 break; 4362 4363 case 3: 4364 // If there are at least three bytes left, then we'll hex-encode the 4365 // next three bytes. Otherwise we'll hex-encode whatever is left. 4366 buffer.append('\\'); 4367 StaticUtils.toHex(valueBytes[i++], buffer); 4368 if (i < valueBytes.length) 4369 { 4370 buffer.append('\\'); 4371 StaticUtils.toHex(valueBytes[i++], buffer); 4372 } 4373 if (i < valueBytes.length) 4374 { 4375 buffer.append('\\'); 4376 StaticUtils.toHex(valueBytes[i], buffer); 4377 } 4378 break; 4379 4380 case 4: 4381 // If there are at least four bytes left, then we'll hex-encode the 4382 // next four bytes. Otherwise we'll hex-encode whatever is left. 4383 buffer.append('\\'); 4384 StaticUtils.toHex(valueBytes[i++], buffer); 4385 if (i < valueBytes.length) 4386 { 4387 buffer.append('\\'); 4388 StaticUtils.toHex(valueBytes[i++], buffer); 4389 } 4390 if (i < valueBytes.length) 4391 { 4392 buffer.append('\\'); 4393 StaticUtils.toHex(valueBytes[i++], buffer); 4394 } 4395 if (i < valueBytes.length) 4396 { 4397 buffer.append('\\'); 4398 StaticUtils.toHex(valueBytes[i], buffer); 4399 } 4400 break; 4401 4402 default: 4403 // We'll hex-encode whatever is left in the buffer. 4404 while (i < valueBytes.length) 4405 { 4406 buffer.append('\\'); 4407 StaticUtils.toHex(valueBytes[i++], buffer); 4408 } 4409 break; 4410 } 4411 } 4412 } 4413 4414 4415 4416 /** 4417 * Appends a number of lines comprising the Java source code that can be used 4418 * to recreate this filter to the given list. Note that unless a first line 4419 * prefix and/or last line suffix are provided, this will just include the 4420 * code for the static method used to create the filter, starting with 4421 * "Filter.createXFilter(" and ending with the closing parenthesis for that 4422 * method call. 4423 * 4424 * @param lineList The list to which the source code lines should be 4425 * added. 4426 * @param indentSpaces The number of spaces that should be used to indent 4427 * the generated code. It must not be negative. 4428 * @param firstLinePrefix An optional string that should precede the static 4429 * method call (e.g., it could be used for an 4430 * attribute assignment, like "Filter f = "). It may 4431 * be {@code null} or empty if there should be no 4432 * first line prefix. 4433 * @param lastLineSuffix An optional suffix that should follow the closing 4434 * parenthesis of the static method call (e.g., it 4435 * could be a semicolon to represent the end of a 4436 * Java statement). It may be {@code null} or empty 4437 * if there should be no last line suffix. 4438 */ 4439 public void toCode(final List<String> lineList, final int indentSpaces, 4440 final String firstLinePrefix, final String lastLineSuffix) 4441 { 4442 // Generate a string with the appropriate indent. 4443 final StringBuilder buffer = new StringBuilder(); 4444 for (int i = 0; i < indentSpaces; i++) 4445 { 4446 buffer.append(' '); 4447 } 4448 final String indent = buffer.toString(); 4449 4450 4451 // Start the first line, including any appropriate prefix. 4452 buffer.setLength(0); 4453 buffer.append(indent); 4454 if (firstLinePrefix != null) 4455 { 4456 buffer.append(firstLinePrefix); 4457 } 4458 4459 4460 // Figure out what type of filter it is and create the appropriate code for 4461 // that type of filter. 4462 switch (filterType) 4463 { 4464 case FILTER_TYPE_AND: 4465 case FILTER_TYPE_OR: 4466 if (filterType == FILTER_TYPE_AND) 4467 { 4468 buffer.append("Filter.createANDFilter("); 4469 } 4470 else 4471 { 4472 buffer.append("Filter.createORFilter("); 4473 } 4474 if (filterComps.length == 0) 4475 { 4476 buffer.append(')'); 4477 if (lastLineSuffix != null) 4478 { 4479 buffer.append(lastLineSuffix); 4480 } 4481 lineList.add(buffer.toString()); 4482 return; 4483 } 4484 4485 for (int i = 0; i < filterComps.length; i++) 4486 { 4487 String suffix; 4488 if (i == (filterComps.length - 1)) 4489 { 4490 suffix = ")"; 4491 if (lastLineSuffix != null) 4492 { 4493 suffix += lastLineSuffix; 4494 } 4495 } 4496 else 4497 { 4498 suffix = ","; 4499 } 4500 4501 filterComps[i].toCode(lineList, indentSpaces + 5, null, suffix); 4502 } 4503 return; 4504 4505 4506 case FILTER_TYPE_NOT: 4507 buffer.append("Filter.createNOTFilter("); 4508 lineList.add(buffer.toString()); 4509 4510 final String suffix; 4511 if (lastLineSuffix == null) 4512 { 4513 suffix = ")"; 4514 } 4515 else 4516 { 4517 suffix = ')' + lastLineSuffix; 4518 } 4519 notComp.toCode(lineList, indentSpaces + 5, null, suffix); 4520 return; 4521 4522 case FILTER_TYPE_PRESENCE: 4523 buffer.append("Filter.createPresenceFilter("); 4524 lineList.add(buffer.toString()); 4525 4526 buffer.setLength(0); 4527 buffer.append(indent); 4528 buffer.append(" \""); 4529 buffer.append(attrName); 4530 buffer.append("\")"); 4531 4532 if (lastLineSuffix != null) 4533 { 4534 buffer.append(lastLineSuffix); 4535 } 4536 4537 lineList.add(buffer.toString()); 4538 return; 4539 4540 4541 case FILTER_TYPE_EQUALITY: 4542 case FILTER_TYPE_GREATER_OR_EQUAL: 4543 case FILTER_TYPE_LESS_OR_EQUAL: 4544 case FILTER_TYPE_APPROXIMATE_MATCH: 4545 if (filterType == FILTER_TYPE_EQUALITY) 4546 { 4547 buffer.append("Filter.createEqualityFilter("); 4548 } 4549 else if (filterType == FILTER_TYPE_GREATER_OR_EQUAL) 4550 { 4551 buffer.append("Filter.createGreaterOrEqualFilter("); 4552 } 4553 else if (filterType == FILTER_TYPE_LESS_OR_EQUAL) 4554 { 4555 buffer.append("Filter.createLessOrEqualFilter("); 4556 } 4557 else 4558 { 4559 buffer.append("Filter.createApproximateMatchFilter("); 4560 } 4561 lineList.add(buffer.toString()); 4562 4563 buffer.setLength(0); 4564 buffer.append(indent); 4565 buffer.append(" \""); 4566 buffer.append(attrName); 4567 buffer.append("\","); 4568 lineList.add(buffer.toString()); 4569 4570 buffer.setLength(0); 4571 buffer.append(indent); 4572 buffer.append(" "); 4573 if (StaticUtils.isSensitiveToCodeAttribute(attrName)) 4574 { 4575 buffer.append("\"---redacted-value---\""); 4576 } 4577 else if (StaticUtils.isPrintableString(assertionValue.getValue())) 4578 { 4579 buffer.append('"'); 4580 buffer.append(assertionValue.stringValue()); 4581 buffer.append('"'); 4582 } 4583 else 4584 { 4585 StaticUtils.byteArrayToCode(assertionValue.getValue(), buffer); 4586 } 4587 4588 buffer.append(')'); 4589 4590 if (lastLineSuffix != null) 4591 { 4592 buffer.append(lastLineSuffix); 4593 } 4594 4595 lineList.add(buffer.toString()); 4596 return; 4597 4598 4599 case FILTER_TYPE_SUBSTRING: 4600 buffer.append("Filter.createSubstringFilter("); 4601 lineList.add(buffer.toString()); 4602 4603 buffer.setLength(0); 4604 buffer.append(indent); 4605 buffer.append(" \""); 4606 buffer.append(attrName); 4607 buffer.append("\","); 4608 lineList.add(buffer.toString()); 4609 4610 final boolean isRedacted = 4611 StaticUtils.isSensitiveToCodeAttribute(attrName); 4612 boolean isPrintable = true; 4613 if (subInitial != null) 4614 { 4615 isPrintable = StaticUtils.isPrintableString(subInitial.getValue()); 4616 } 4617 4618 if (isPrintable && (subAny != null)) 4619 { 4620 for (final ASN1OctetString s : subAny) 4621 { 4622 if (! StaticUtils.isPrintableString(s.getValue())) 4623 { 4624 isPrintable = false; 4625 break; 4626 } 4627 } 4628 } 4629 4630 if (isPrintable && (subFinal != null)) 4631 { 4632 isPrintable = StaticUtils.isPrintableString(subFinal.getValue()); 4633 } 4634 4635 buffer.setLength(0); 4636 buffer.append(indent); 4637 buffer.append(" "); 4638 if (subInitial == null) 4639 { 4640 buffer.append("null"); 4641 } 4642 else if (isRedacted) 4643 { 4644 buffer.append("\"---redacted-subInitial---\""); 4645 } 4646 else if (isPrintable) 4647 { 4648 buffer.append('"'); 4649 buffer.append(subInitial.stringValue()); 4650 buffer.append('"'); 4651 } 4652 else 4653 { 4654 StaticUtils.byteArrayToCode(subInitial.getValue(), buffer); 4655 } 4656 buffer.append(','); 4657 lineList.add(buffer.toString()); 4658 4659 buffer.setLength(0); 4660 buffer.append(indent); 4661 buffer.append(" "); 4662 if ((subAny == null) || (subAny.length == 0)) 4663 { 4664 buffer.append("null,"); 4665 lineList.add(buffer.toString()); 4666 } 4667 else if (isRedacted) 4668 { 4669 buffer.append("new String[]"); 4670 lineList.add(buffer.toString()); 4671 4672 lineList.add(indent + " {"); 4673 4674 for (int i=0; i < subAny.length; i++) 4675 { 4676 buffer.setLength(0); 4677 buffer.append(indent); 4678 buffer.append(" \"---redacted-subAny-"); 4679 buffer.append(i+1); 4680 buffer.append("---\""); 4681 if (i < (subAny.length-1)) 4682 { 4683 buffer.append(','); 4684 } 4685 lineList.add(buffer.toString()); 4686 } 4687 4688 lineList.add(indent + " },"); 4689 } 4690 else if (isPrintable) 4691 { 4692 buffer.append("new String[]"); 4693 lineList.add(buffer.toString()); 4694 4695 lineList.add(indent + " {"); 4696 4697 for (int i=0; i < subAny.length; i++) 4698 { 4699 buffer.setLength(0); 4700 buffer.append(indent); 4701 buffer.append(" \""); 4702 buffer.append(subAny[i].stringValue()); 4703 buffer.append('"'); 4704 if (i < (subAny.length-1)) 4705 { 4706 buffer.append(','); 4707 } 4708 lineList.add(buffer.toString()); 4709 } 4710 4711 lineList.add(indent + " },"); 4712 } 4713 else 4714 { 4715 buffer.append("new String[]"); 4716 lineList.add(buffer.toString()); 4717 4718 lineList.add(indent + " {"); 4719 4720 for (int i=0; i < subAny.length; i++) 4721 { 4722 buffer.setLength(0); 4723 buffer.append(indent); 4724 buffer.append(" "); 4725 StaticUtils.byteArrayToCode(subAny[i].getValue(), buffer); 4726 if (i < (subAny.length-1)) 4727 { 4728 buffer.append(','); 4729 } 4730 lineList.add(buffer.toString()); 4731 } 4732 4733 lineList.add(indent + " },"); 4734 } 4735 4736 buffer.setLength(0); 4737 buffer.append(indent); 4738 buffer.append(" "); 4739 if (subFinal == null) 4740 { 4741 buffer.append("null)"); 4742 } 4743 else if (isRedacted) 4744 { 4745 buffer.append("\"---redacted-subFinal---\")"); 4746 } 4747 else if (isPrintable) 4748 { 4749 buffer.append('"'); 4750 buffer.append(subFinal.stringValue()); 4751 buffer.append("\")"); 4752 } 4753 else 4754 { 4755 StaticUtils.byteArrayToCode(subFinal.getValue(), buffer); 4756 buffer.append(')'); 4757 } 4758 if (lastLineSuffix != null) 4759 { 4760 buffer.append(lastLineSuffix); 4761 } 4762 lineList.add(buffer.toString()); 4763 return; 4764 4765 4766 case FILTER_TYPE_EXTENSIBLE_MATCH: 4767 buffer.append("Filter.createExtensibleMatchFilter("); 4768 lineList.add(buffer.toString()); 4769 4770 buffer.setLength(0); 4771 buffer.append(indent); 4772 buffer.append(" "); 4773 if (attrName == null) 4774 { 4775 buffer.append("null, // Attribute Description"); 4776 } 4777 else 4778 { 4779 buffer.append('"'); 4780 buffer.append(attrName); 4781 buffer.append("\","); 4782 } 4783 lineList.add(buffer.toString()); 4784 4785 buffer.setLength(0); 4786 buffer.append(indent); 4787 buffer.append(" "); 4788 if (matchingRuleID == null) 4789 { 4790 buffer.append("null, // Matching Rule ID"); 4791 } 4792 else 4793 { 4794 buffer.append('"'); 4795 buffer.append(matchingRuleID); 4796 buffer.append("\","); 4797 } 4798 lineList.add(buffer.toString()); 4799 4800 buffer.setLength(0); 4801 buffer.append(indent); 4802 buffer.append(" "); 4803 buffer.append(dnAttributes); 4804 buffer.append(", // DN Attributes"); 4805 lineList.add(buffer.toString()); 4806 4807 buffer.setLength(0); 4808 buffer.append(indent); 4809 buffer.append(" "); 4810 if ((attrName != null) && 4811 StaticUtils.isSensitiveToCodeAttribute(attrName)) 4812 { 4813 buffer.append("\"---redacted-value---\")"); 4814 } 4815 else 4816 { 4817 if (StaticUtils.isPrintableString(assertionValue.getValue())) 4818 { 4819 buffer.append('"'); 4820 buffer.append(assertionValue.stringValue()); 4821 buffer.append("\")"); 4822 } 4823 else 4824 { 4825 StaticUtils.byteArrayToCode(assertionValue.getValue(), buffer); 4826 buffer.append(')'); 4827 } 4828 } 4829 4830 if (lastLineSuffix != null) 4831 { 4832 buffer.append(lastLineSuffix); 4833 } 4834 lineList.add(buffer.toString()); 4835 return; 4836 } 4837 } 4838}