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