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