001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.protocols.ldap; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashSet; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.StringTokenizer; 025 026import org.forgerock.i18n.LocalizableMessage; 027import org.forgerock.i18n.slf4j.LocalizedLogger; 028import org.forgerock.opendj.ldap.ByteString; 029import org.forgerock.opendj.ldap.ByteStringBuilder; 030import org.forgerock.opendj.ldap.ResultCode; 031import org.forgerock.opendj.ldap.schema.AttributeType; 032import org.forgerock.opendj.ldap.schema.MatchingRule; 033import org.opends.server.core.DirectoryServer; 034import org.opends.server.types.DirectoryException; 035import org.opends.server.types.FilterType; 036import org.opends.server.types.LDAPException; 037import org.opends.server.types.RawFilter; 038import org.opends.server.types.SearchFilter; 039 040import static org.opends.messages.ProtocolMessages.*; 041import static org.opends.server.util.StaticUtils.*; 042 043/** 044 * This class defines the data structures and methods to use when interacting 045 * with an LDAP search filter, which defines a set of criteria for locating 046 * entries in a search request. 047 */ 048public class LDAPFilter 049 extends RawFilter 050{ 051 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 052 053 private static LDAPFilter objectClassPresent; 054 055 /** The set of subAny elements for substring filters. */ 056 private ArrayList<ByteString> subAnyElements; 057 058 /** The set of filter components for AND and OR filters. */ 059 private ArrayList<RawFilter> filterComponents; 060 061 /** Indicates whether to match on DN attributes for extensible match filters. */ 062 private boolean dnAttributes; 063 064 /** The assertion value for several filter types. */ 065 private ByteString assertionValue; 066 067 /** The subFinal element for substring filters. */ 068 private ByteString subFinalElement; 069 070 /** The subInitial element for substring filters. */ 071 private ByteString subInitialElement; 072 073 /** The filter type for this filter. */ 074 private FilterType filterType; 075 076 /** The filter component for NOT filters. */ 077 private RawFilter notComponent; 078 079 /** The attribute type for several filter types. */ 080 private String attributeType; 081 082 /** The matching rule ID for extensible matching filters. */ 083 private String matchingRuleID; 084 085 086 087 /** 088 * Creates a new LDAP filter with the provided information. Note that this 089 * constructor is only intended for use by the {@code RawFilter} class and any 090 * use of this constructor outside of that class must be very careful to 091 * ensure that all of the appropriate element types have been provided for the 092 * associated filter type. 093 * 094 * @param filterType The filter type for this filter. 095 * @param filterComponents The filter components for AND and OR filters. 096 * @param notComponent The filter component for NOT filters. 097 * @param attributeType The attribute type for this filter. 098 * @param assertionValue The assertion value for this filter. 099 * @param subInitialElement The subInitial element for substring filters. 100 * @param subAnyElements The subAny elements for substring filters. 101 * @param subFinalElement The subFinal element for substring filters. 102 * @param matchingRuleID The matching rule ID for extensible filters. 103 * @param dnAttributes The dnAttributes flag for extensible filters. 104 */ 105 public LDAPFilter(FilterType filterType, 106 ArrayList<RawFilter> filterComponents, 107 RawFilter notComponent, String attributeType, 108 ByteString assertionValue, ByteString subInitialElement, 109 ArrayList<ByteString> subAnyElements, 110 ByteString subFinalElement, String matchingRuleID, 111 boolean dnAttributes) 112 { 113 this.filterType = filterType; 114 this.filterComponents = filterComponents; 115 this.notComponent = notComponent; 116 this.attributeType = attributeType; 117 this.assertionValue = assertionValue; 118 this.subInitialElement = subInitialElement; 119 this.subAnyElements = subAnyElements; 120 this.subFinalElement = subFinalElement; 121 this.matchingRuleID = matchingRuleID; 122 this.dnAttributes = dnAttributes; 123 } 124 125 126 127 /** 128 * Creates a new LDAP filter from the provided search filter. 129 * 130 * @param filter The search filter to use to create this LDAP filter. 131 */ 132 public LDAPFilter(SearchFilter filter) 133 { 134 this.filterType = filter.getFilterType(); 135 136 switch (filterType) 137 { 138 case AND: 139 case OR: 140 Collection<SearchFilter> comps = filter.getFilterComponents(); 141 filterComponents = new ArrayList<>(comps.size()); 142 for (SearchFilter f : comps) 143 { 144 filterComponents.add(new LDAPFilter(f)); 145 } 146 147 notComponent = null; 148 attributeType = null; 149 assertionValue = null; 150 subInitialElement = null; 151 subAnyElements = null; 152 subFinalElement = null; 153 matchingRuleID = null; 154 dnAttributes = false; 155 break; 156 case NOT: 157 notComponent = new LDAPFilter(filter.getNotComponent()); 158 159 filterComponents = null; 160 attributeType = null; 161 assertionValue = null; 162 subInitialElement = null; 163 subAnyElements = null; 164 subFinalElement = null; 165 matchingRuleID = null; 166 dnAttributes = false; 167 break; 168 case EQUALITY: 169 case GREATER_OR_EQUAL: 170 case LESS_OR_EQUAL: 171 case APPROXIMATE_MATCH: 172 attributeType = filter.getAttributeType().getNameOrOID(); 173 assertionValue = filter.getAssertionValue(); 174 175 filterComponents = null; 176 notComponent = null; 177 subInitialElement = null; 178 subAnyElements = null; 179 subFinalElement = null; 180 matchingRuleID = null; 181 dnAttributes = false; 182 break; 183 case SUBSTRING: 184 attributeType = filter.getAttributeType().getNameOrOID(); 185 186 ByteString bs = filter.getSubInitialElement(); 187 if (bs == null) 188 { 189 subInitialElement = null; 190 } 191 else 192 { 193 subInitialElement = bs; 194 } 195 196 bs = filter.getSubFinalElement(); 197 if (bs == null) 198 { 199 subFinalElement = null; 200 } 201 else 202 { 203 subFinalElement = bs; 204 } 205 206 List<ByteString> subAnyStrings = filter.getSubAnyElements(); 207 if (subAnyStrings == null) 208 { 209 subAnyElements = null; 210 } 211 else 212 { 213 subAnyElements = new ArrayList<>(subAnyStrings); 214 } 215 216 filterComponents = null; 217 notComponent = null; 218 assertionValue = null; 219 matchingRuleID = null; 220 dnAttributes = false; 221 break; 222 case PRESENT: 223 attributeType = filter.getAttributeType().getNameOrOID(); 224 225 filterComponents = null; 226 notComponent = null; 227 assertionValue = null; 228 subInitialElement = null; 229 subAnyElements = null; 230 subFinalElement = null; 231 matchingRuleID = null; 232 dnAttributes = false; 233 break; 234 case EXTENSIBLE_MATCH: 235 dnAttributes = filter.getDNAttributes(); 236 matchingRuleID = filter.getMatchingRuleID(); 237 238 AttributeType attrType = filter.getAttributeType(); 239 if (attrType == null) 240 { 241 attributeType = null; 242 } 243 else 244 { 245 attributeType = attrType.getNameOrOID(); 246 } 247 248 assertionValue = filter.getAssertionValue(); 249 filterComponents = null; 250 notComponent = null; 251 subInitialElement = null; 252 subAnyElements = null; 253 subFinalElement = null; 254 break; 255 } 256 } 257 258 259 260 /** 261 * Decodes the provided string into an LDAP search filter. 262 * 263 * @param filterString The string representation of the search filter to 264 * decode. 265 * 266 * @return The decoded LDAP search filter. 267 * 268 * @throws LDAPException If the provided string does not represent a valid 269 * LDAP search filter. 270 */ 271 public static LDAPFilter decode(String filterString) 272 throws LDAPException 273 { 274 if (filterString == null) 275 { 276 LocalizableMessage message = ERR_LDAP_FILTER_STRING_NULL.get(); 277 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 278 } 279 280 281 try 282 { 283 return decode(filterString, 0, filterString.length()); 284 } 285 catch (LDAPException le) 286 { 287 logger.traceException(le); 288 289 throw le; 290 } 291 catch (Exception e) 292 { 293 logger.traceException(e); 294 295 LocalizableMessage message = ERR_LDAP_FILTER_UNCAUGHT_EXCEPTION.get(filterString, e); 296 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 297 } 298 } 299 300 301 302 /** 303 * Decodes the provided string into an LDAP search filter. 304 * 305 * @param filterString The string representation of the search filter to 306 * decode. 307 * @param startPos The position of the first character in the filter 308 * to parse. 309 * @param endPos The position of the first character after the end of 310 * the filter to parse. 311 * 312 * @return The decoded LDAP search filter. 313 * 314 * @throws LDAPException If the provided string does not represent a valid 315 * LDAP search filter. 316 */ 317 private static LDAPFilter decode(String filterString, int startPos, 318 int endPos) 319 throws LDAPException 320 { 321 // Make sure that the length is sufficient for a valid search filter. 322 int length = endPos - startPos; 323 if (length <= 0) 324 { 325 LocalizableMessage message = ERR_LDAP_FILTER_STRING_NULL.get(); 326 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 327 } 328 329 // If the filter is enclosed in a pair of apostrophes ("single-quotes") it 330 // is invalid (issue #1024). 331 if (1 < filterString.length() 332 && filterString.startsWith("'") && filterString.endsWith("'")) 333 { 334 LocalizableMessage message = 335 ERR_LDAP_FILTER_ENCLOSED_IN_APOSTROPHES.get(filterString); 336 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 337 } 338 339 // If the filter is surrounded by parentheses (which it should be), then 340 // strip them off. 341 if (filterString.charAt(startPos) == '(') 342 { 343 if (filterString.charAt(endPos-1) == ')') 344 { 345 startPos++; 346 endPos--; 347 } 348 else 349 { 350 LocalizableMessage message = ERR_LDAP_FILTER_MISMATCHED_PARENTHESES.get( 351 filterString, startPos, endPos); 352 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 353 } 354 } 355 356 357 // Look at the first character. If it is a '&' then it is an AND search. 358 // If it is a '|' then it is an OR search. If it is a '!' then it is a NOT 359 // search. 360 char c = filterString.charAt(startPos); 361 if (c == '&') 362 { 363 return decodeCompoundFilter(FilterType.AND, filterString, startPos+1, 364 endPos); 365 } 366 else if (c == '|') 367 { 368 return decodeCompoundFilter(FilterType.OR, filterString, startPos+1, 369 endPos); 370 } 371 else if (c == '!') 372 { 373 return decodeCompoundFilter(FilterType.NOT, filterString, startPos+1, 374 endPos); 375 } 376 377 378 // If we've gotten here, then it must be a simple filter. It must have an 379 // equal sign at some point, so find it. 380 int equalPos = -1; 381 for (int i=startPos; i < endPos; i++) 382 { 383 if (filterString.charAt(i) == '=') 384 { 385 equalPos = i; 386 break; 387 } 388 } 389 390 if (equalPos <= startPos) 391 { 392 LocalizableMessage message = 393 ERR_LDAP_FILTER_NO_EQUAL_SIGN.get(filterString, startPos, endPos); 394 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 395 } 396 397 398 // Look at the character immediately before the equal sign, because it may 399 // help determine the filter type. 400 int attrEndPos; 401 FilterType filterType; 402 switch (filterString.charAt(equalPos-1)) 403 { 404 case '~': 405 filterType = FilterType.APPROXIMATE_MATCH; 406 attrEndPos = equalPos-1; 407 break; 408 case '>': 409 filterType = FilterType.GREATER_OR_EQUAL; 410 attrEndPos = equalPos-1; 411 break; 412 case '<': 413 filterType = FilterType.LESS_OR_EQUAL; 414 attrEndPos = equalPos-1; 415 break; 416 case ':': 417 return decodeExtensibleMatchFilter(filterString, startPos, equalPos, 418 endPos); 419 default: 420 filterType = FilterType.EQUALITY; 421 attrEndPos = equalPos; 422 break; 423 } 424 425 426 // The part of the filter string before the equal sign should be the 427 // attribute type. Make sure that the characters it contains are acceptable 428 // for attribute types, including those allowed by attribute name 429 // exceptions (ASCII letters and digits, the dash, and the underscore). We 430 // also need to allow attribute options, which includes the semicolon and 431 // the equal sign. 432 String attrType = filterString.substring(startPos, attrEndPos); 433 for (int i=0; i < attrType.length(); i++) 434 { 435 switch (attrType.charAt(i)) 436 { 437 case '-': 438 case '0': 439 case '1': 440 case '2': 441 case '3': 442 case '4': 443 case '5': 444 case '6': 445 case '7': 446 case '8': 447 case '9': 448 case ';': 449 case '=': 450 case 'A': 451 case 'B': 452 case 'C': 453 case 'D': 454 case 'E': 455 case 'F': 456 case 'G': 457 case 'H': 458 case 'I': 459 case 'J': 460 case 'K': 461 case 'L': 462 case 'M': 463 case 'N': 464 case 'O': 465 case 'P': 466 case 'Q': 467 case 'R': 468 case 'S': 469 case 'T': 470 case 'U': 471 case 'V': 472 case 'W': 473 case 'X': 474 case 'Y': 475 case 'Z': 476 case '_': 477 case 'a': 478 case 'b': 479 case 'c': 480 case 'd': 481 case 'e': 482 case 'f': 483 case 'g': 484 case 'h': 485 case 'i': 486 case 'j': 487 case 'k': 488 case 'l': 489 case 'm': 490 case 'n': 491 case 'o': 492 case 'p': 493 case 'q': 494 case 'r': 495 case 's': 496 case 't': 497 case 'u': 498 case 'v': 499 case 'w': 500 case 'x': 501 case 'y': 502 case 'z': 503 // These are all OK. 504 break; 505 506 case '.': 507 case '/': 508 case ':': 509 case '<': 510 case '>': 511 case '?': 512 case '@': 513 case '[': 514 case '\\': 515 case ']': 516 case '^': 517 case '`': 518 // These are not allowed, but they are explicitly called out because 519 // they are included in the range of values between '-' and 'z', and 520 // making sure all possible characters are included can help make the 521 // switch statement more efficient. We'll fall through to the default 522 // clause to reject them. 523 default: 524 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_CHAR_IN_ATTR_TYPE.get( 525 attrType, attrType.charAt(i), i); 526 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 527 } 528 } 529 530 531 // Get the attribute value. 532 String valueStr = filterString.substring(equalPos+1, endPos); 533 if (valueStr.length() == 0) 534 { 535 return new LDAPFilter(filterType, null, null, attrType, 536 ByteString.empty(), null, null, null, null, 537 false); 538 } 539 else if (valueStr.equals("*")) 540 { 541 return new LDAPFilter(FilterType.PRESENT, null, null, attrType, null, 542 null, null, null, null, false); 543 } 544 else if (valueStr.indexOf('*') >= 0) 545 { 546 return decodeSubstringFilter(filterString, attrType, equalPos, endPos); 547 } 548 else 549 { 550 boolean hasEscape = false; 551 byte[] valueBytes = getBytes(valueStr); 552 for (byte valueByte : valueBytes) 553 { 554 if (valueByte == 0x5C) // The backslash character 555 { 556 hasEscape = true; 557 break; 558 } 559 } 560 561 ByteString value; 562 if (hasEscape) 563 { 564 ByteStringBuilder valueBuffer = 565 new ByteStringBuilder(valueStr.length()); 566 for (int i=0; i < valueBytes.length; i++) 567 { 568 if (valueBytes[i] == 0x5C) // The backslash character 569 { 570 // The next two bytes must be the hex characters that comprise the 571 // binary value. 572 if (i + 2 >= valueBytes.length) 573 { 574 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 575 filterString, equalPos+i+1); 576 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 577 } 578 579 byte byteValue = 0; 580 switch (valueBytes[++i]) 581 { 582 case 0x30: // '0' 583 break; 584 case 0x31: // '1' 585 byteValue = (byte) 0x10; 586 break; 587 case 0x32: // '2' 588 byteValue = (byte) 0x20; 589 break; 590 case 0x33: // '3' 591 byteValue = (byte) 0x30; 592 break; 593 case 0x34: // '4' 594 byteValue = (byte) 0x40; 595 break; 596 case 0x35: // '5' 597 byteValue = (byte) 0x50; 598 break; 599 case 0x36: // '6' 600 byteValue = (byte) 0x60; 601 break; 602 case 0x37: // '7' 603 byteValue = (byte) 0x70; 604 break; 605 case 0x38: // '8' 606 byteValue = (byte) 0x80; 607 break; 608 case 0x39: // '9' 609 byteValue = (byte) 0x90; 610 break; 611 case 0x41: // 'A' 612 case 0x61: // 'a' 613 byteValue = (byte) 0xA0; 614 break; 615 case 0x42: // 'B' 616 case 0x62: // 'b' 617 byteValue = (byte) 0xB0; 618 break; 619 case 0x43: // 'C' 620 case 0x63: // 'c' 621 byteValue = (byte) 0xC0; 622 break; 623 case 0x44: // 'D' 624 case 0x64: // 'd' 625 byteValue = (byte) 0xD0; 626 break; 627 case 0x45: // 'E' 628 case 0x65: // 'e' 629 byteValue = (byte) 0xE0; 630 break; 631 case 0x46: // 'F' 632 case 0x66: // 'f' 633 byteValue = (byte) 0xF0; 634 break; 635 default: 636 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 637 filterString, equalPos+i+1); 638 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 639 } 640 641 switch (valueBytes[++i]) 642 { 643 case 0x30: // '0' 644 break; 645 case 0x31: // '1' 646 byteValue |= (byte) 0x01; 647 break; 648 case 0x32: // '2' 649 byteValue |= (byte) 0x02; 650 break; 651 case 0x33: // '3' 652 byteValue |= (byte) 0x03; 653 break; 654 case 0x34: // '4' 655 byteValue |= (byte) 0x04; 656 break; 657 case 0x35: // '5' 658 byteValue |= (byte) 0x05; 659 break; 660 case 0x36: // '6' 661 byteValue |= (byte) 0x06; 662 break; 663 case 0x37: // '7' 664 byteValue |= (byte) 0x07; 665 break; 666 case 0x38: // '8' 667 byteValue |= (byte) 0x08; 668 break; 669 case 0x39: // '9' 670 byteValue |= (byte) 0x09; 671 break; 672 case 0x41: // 'A' 673 case 0x61: // 'a' 674 byteValue |= (byte) 0x0A; 675 break; 676 case 0x42: // 'B' 677 case 0x62: // 'b' 678 byteValue |= (byte) 0x0B; 679 break; 680 case 0x43: // 'C' 681 case 0x63: // 'c' 682 byteValue |= (byte) 0x0C; 683 break; 684 case 0x44: // 'D' 685 case 0x64: // 'd' 686 byteValue |= (byte) 0x0D; 687 break; 688 case 0x45: // 'E' 689 case 0x65: // 'e' 690 byteValue |= (byte) 0x0E; 691 break; 692 case 0x46: // 'F' 693 case 0x66: // 'f' 694 byteValue |= (byte) 0x0F; 695 break; 696 default: 697 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 698 filterString, equalPos+i+1); 699 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 700 } 701 702 valueBuffer.appendByte(byteValue); 703 } 704 else 705 { 706 valueBuffer.appendByte(valueBytes[i]); 707 } 708 } 709 710 value = valueBuffer.toByteString(); 711 } 712 else 713 { 714 value = ByteString.wrap(valueBytes); 715 } 716 717 return new LDAPFilter(filterType, null, null, attrType, value, null, null, 718 null, null, false); 719 } 720 } 721 722 723 724 /** 725 * Decodes a set of filters from the provided filter string within the 726 * indicated range. 727 * 728 * @param filterType The filter type for this compound filter. It must be 729 * an AND, OR or NOT filter. 730 * @param filterString The string containing the filter information to 731 * decode. 732 * @param startPos The position of the first character in the set of 733 * filters to decode. 734 * @param endPos The position of the first character after the end of 735 * the set of filters to decode. 736 * 737 * @return The decoded LDAP filter. 738 * 739 * @throws LDAPException If a problem occurs while attempting to decode the 740 * compound filter. 741 */ 742 private static LDAPFilter decodeCompoundFilter(FilterType filterType, 743 String filterString, 744 int startPos, int endPos) 745 throws LDAPException 746 { 747 // Create a list to hold the returned components. 748 ArrayList<RawFilter> filterComponents = new ArrayList<>(); 749 750 751 // If the end pos is equal to the start pos, then there are no components. 752 if (startPos == endPos) 753 { 754 if (filterType == FilterType.NOT) 755 { 756 LocalizableMessage message = 757 ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos); 758 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 759 } 760 else 761 { 762 // This is valid and will be treated as a TRUE/FALSE filter. 763 return new LDAPFilter(filterType, filterComponents, null, null, null, 764 null, null, null, null, false); 765 } 766 } 767 768 769 // The first and last characters must be parentheses. If not, then that's 770 // an error. 771 if (filterString.charAt(startPos) != '(' || 772 filterString.charAt(endPos-1) != ')') 773 { 774 LocalizableMessage message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get( 775 filterString, startPos, endPos); 776 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 777 } 778 779 780 // Iterate through the characters in the value. Whenever an open 781 // parenthesis is found, locate the corresponding close parenthesis by 782 // counting the number of intermediate open/close parentheses. 783 int pendingOpens = 0; 784 int openPos = -1; 785 for (int i=startPos; i < endPos; i++) 786 { 787 char c = filterString.charAt(i); 788 if (c == '(') 789 { 790 if (openPos < 0) 791 { 792 openPos = i; 793 } 794 795 pendingOpens++; 796 } 797 else if (c == ')') 798 { 799 pendingOpens--; 800 if (pendingOpens == 0) 801 { 802 filterComponents.add(decode(filterString, openPos, i+1)); 803 openPos = -1; 804 } 805 else if (pendingOpens < 0) 806 { 807 LocalizableMessage message = ERR_LDAP_FILTER_NO_CORRESPONDING_OPEN_PARENTHESIS. 808 get(filterString, i); 809 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 810 } 811 } 812 else if (pendingOpens <= 0) 813 { 814 LocalizableMessage message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get( 815 filterString, startPos, endPos); 816 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 817 } 818 } 819 820 821 // At this point, we have parsed the entire set of filter components. The 822 // list of open parenthesis positions must be empty. 823 if (pendingOpens != 0) 824 { 825 LocalizableMessage message = ERR_LDAP_FILTER_NO_CORRESPONDING_CLOSE_PARENTHESIS.get( 826 filterString, openPos); 827 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 828 } 829 830 831 // We should have everything we need, so return the list. 832 if (filterType == FilterType.NOT) 833 { 834 if (filterComponents.size() != 1) 835 { 836 LocalizableMessage message = 837 ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos); 838 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 839 } 840 RawFilter notComponent = filterComponents.get(0); 841 return new LDAPFilter(filterType, null, notComponent, null, null, 842 null, null, null, null, false); 843 } 844 else 845 { 846 return new LDAPFilter(filterType, filterComponents, null, null, null, 847 null, null, null, null, false); 848 } 849 } 850 851 852 853 /** 854 * Decodes a substring search filter component based on the provided 855 * information. 856 * 857 * @param filterString The filter string containing the information to 858 * decode. 859 * @param attrType The attribute type for this substring filter 860 * component. 861 * @param equalPos The location of the equal sign separating the 862 * attribute type from the value. 863 * @param endPos The position of the first character after the end of 864 * the substring value. 865 * 866 * @return The decoded LDAP filter. 867 * 868 * @throws LDAPException If a problem occurs while attempting to decode the 869 * substring filter. 870 */ 871 private static LDAPFilter decodeSubstringFilter(String filterString, 872 String attrType, int equalPos, 873 int endPos) 874 throws LDAPException 875 { 876 // Get a binary representation of the value. 877 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos)); 878 879 880 // Find the locations of all the asterisks in the value. Also, check to 881 // see if there are any escaped values, since they will need special 882 // treatment. 883 boolean hasEscape = false; 884 LinkedList<Integer> asteriskPositions = new LinkedList<>(); 885 for (int i=0; i < valueBytes.length; i++) 886 { 887 if (valueBytes[i] == 0x2A) // The asterisk. 888 { 889 asteriskPositions.add(i); 890 } 891 else if (valueBytes[i] == 0x5C) // The backslash. 892 { 893 hasEscape = true; 894 } 895 } 896 897 898 // If there were no asterisks, then this isn't a substring filter. 899 if (asteriskPositions.isEmpty()) 900 { 901 LocalizableMessage message = ERR_LDAP_FILTER_SUBSTRING_NO_ASTERISKS.get( 902 filterString, equalPos+1, endPos); 903 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 904 } 905 906 907 // If the value starts with an asterisk, then there is no subInitial 908 // component. Otherwise, parse out the subInitial. 909 ByteString subInitial; 910 int firstPos = asteriskPositions.removeFirst(); 911 if (firstPos == 0) 912 { 913 subInitial = null; 914 } 915 else 916 { 917 if (hasEscape) 918 { 919 ByteStringBuilder buffer = new ByteStringBuilder(firstPos); 920 for (int i=0; i < firstPos; i++) 921 { 922 if (valueBytes[i] == 0x5C) 923 { 924 // The next two bytes must be the hex characters that comprise the 925 // binary value. 926 if (i + 2 >= valueBytes.length) 927 { 928 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 929 filterString, equalPos+i+1); 930 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 931 } 932 933 byte byteValue = 0; 934 switch (valueBytes[++i]) 935 { 936 case 0x30: // '0' 937 break; 938 case 0x31: // '1' 939 byteValue = (byte) 0x10; 940 break; 941 case 0x32: // '2' 942 byteValue = (byte) 0x20; 943 break; 944 case 0x33: // '3' 945 byteValue = (byte) 0x30; 946 break; 947 case 0x34: // '4' 948 byteValue = (byte) 0x40; 949 break; 950 case 0x35: // '5' 951 byteValue = (byte) 0x50; 952 break; 953 case 0x36: // '6' 954 byteValue = (byte) 0x60; 955 break; 956 case 0x37: // '7' 957 byteValue = (byte) 0x70; 958 break; 959 case 0x38: // '8' 960 byteValue = (byte) 0x80; 961 break; 962 case 0x39: // '9' 963 byteValue = (byte) 0x90; 964 break; 965 case 0x41: // 'A' 966 case 0x61: // 'a' 967 byteValue = (byte) 0xA0; 968 break; 969 case 0x42: // 'B' 970 case 0x62: // 'b' 971 byteValue = (byte) 0xB0; 972 break; 973 case 0x43: // 'C' 974 case 0x63: // 'c' 975 byteValue = (byte) 0xC0; 976 break; 977 case 0x44: // 'D' 978 case 0x64: // 'd' 979 byteValue = (byte) 0xD0; 980 break; 981 case 0x45: // 'E' 982 case 0x65: // 'e' 983 byteValue = (byte) 0xE0; 984 break; 985 case 0x46: // 'F' 986 case 0x66: // 'f' 987 byteValue = (byte) 0xF0; 988 break; 989 default: 990 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 991 filterString, equalPos+i+1); 992 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 993 } 994 995 switch (valueBytes[++i]) 996 { 997 case 0x30: // '0' 998 break; 999 case 0x31: // '1' 1000 byteValue |= (byte) 0x01; 1001 break; 1002 case 0x32: // '2' 1003 byteValue |= (byte) 0x02; 1004 break; 1005 case 0x33: // '3' 1006 byteValue |= (byte) 0x03; 1007 break; 1008 case 0x34: // '4' 1009 byteValue |= (byte) 0x04; 1010 break; 1011 case 0x35: // '5' 1012 byteValue |= (byte) 0x05; 1013 break; 1014 case 0x36: // '6' 1015 byteValue |= (byte) 0x06; 1016 break; 1017 case 0x37: // '7' 1018 byteValue |= (byte) 0x07; 1019 break; 1020 case 0x38: // '8' 1021 byteValue |= (byte) 0x08; 1022 break; 1023 case 0x39: // '9' 1024 byteValue |= (byte) 0x09; 1025 break; 1026 case 0x41: // 'A' 1027 case 0x61: // 'a' 1028 byteValue |= (byte) 0x0A; 1029 break; 1030 case 0x42: // 'B' 1031 case 0x62: // 'b' 1032 byteValue |= (byte) 0x0B; 1033 break; 1034 case 0x43: // 'C' 1035 case 0x63: // 'c' 1036 byteValue |= (byte) 0x0C; 1037 break; 1038 case 0x44: // 'D' 1039 case 0x64: // 'd' 1040 byteValue |= (byte) 0x0D; 1041 break; 1042 case 0x45: // 'E' 1043 case 0x65: // 'e' 1044 byteValue |= (byte) 0x0E; 1045 break; 1046 case 0x46: // 'F' 1047 case 0x66: // 'f' 1048 byteValue |= (byte) 0x0F; 1049 break; 1050 default: 1051 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1052 filterString, equalPos+i+1); 1053 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1054 } 1055 1056 buffer.appendByte(byteValue); 1057 } 1058 else 1059 { 1060 buffer.appendByte(valueBytes[i]); 1061 } 1062 } 1063 1064 subInitial = buffer.toByteString(); 1065 } 1066 else 1067 { 1068 subInitial = ByteString.wrap(valueBytes, 0, firstPos); 1069 } 1070 } 1071 1072 1073 // Next, process through the rest of the asterisks to get the subAny values. 1074 ArrayList<ByteString> subAny = new ArrayList<>(); 1075 for (int asteriskPos : asteriskPositions) 1076 { 1077 int length = asteriskPos - firstPos - 1; 1078 1079 if (hasEscape) 1080 { 1081 ByteStringBuilder buffer = new ByteStringBuilder(length); 1082 for (int i=firstPos+1; i < asteriskPos; i++) 1083 { 1084 if (valueBytes[i] == 0x5C) 1085 { 1086 // The next two bytes must be the hex characters that comprise the 1087 // binary value. 1088 if (i + 2 >= valueBytes.length) 1089 { 1090 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1091 filterString, equalPos+i+1); 1092 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1093 } 1094 1095 byte byteValue = 0; 1096 switch (valueBytes[++i]) 1097 { 1098 case 0x30: // '0' 1099 break; 1100 case 0x31: // '1' 1101 byteValue = (byte) 0x10; 1102 break; 1103 case 0x32: // '2' 1104 byteValue = (byte) 0x20; 1105 break; 1106 case 0x33: // '3' 1107 byteValue = (byte) 0x30; 1108 break; 1109 case 0x34: // '4' 1110 byteValue = (byte) 0x40; 1111 break; 1112 case 0x35: // '5' 1113 byteValue = (byte) 0x50; 1114 break; 1115 case 0x36: // '6' 1116 byteValue = (byte) 0x60; 1117 break; 1118 case 0x37: // '7' 1119 byteValue = (byte) 0x70; 1120 break; 1121 case 0x38: // '8' 1122 byteValue = (byte) 0x80; 1123 break; 1124 case 0x39: // '9' 1125 byteValue = (byte) 0x90; 1126 break; 1127 case 0x41: // 'A' 1128 case 0x61: // 'a' 1129 byteValue = (byte) 0xA0; 1130 break; 1131 case 0x42: // 'B' 1132 case 0x62: // 'b' 1133 byteValue = (byte) 0xB0; 1134 break; 1135 case 0x43: // 'C' 1136 case 0x63: // 'c' 1137 byteValue = (byte) 0xC0; 1138 break; 1139 case 0x44: // 'D' 1140 case 0x64: // 'd' 1141 byteValue = (byte) 0xD0; 1142 break; 1143 case 0x45: // 'E' 1144 case 0x65: // 'e' 1145 byteValue = (byte) 0xE0; 1146 break; 1147 case 0x46: // 'F' 1148 case 0x66: // 'f' 1149 byteValue = (byte) 0xF0; 1150 break; 1151 default: 1152 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1153 filterString, equalPos+i+1); 1154 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1155 } 1156 1157 switch (valueBytes[++i]) 1158 { 1159 case 0x30: // '0' 1160 break; 1161 case 0x31: // '1' 1162 byteValue |= (byte) 0x01; 1163 break; 1164 case 0x32: // '2' 1165 byteValue |= (byte) 0x02; 1166 break; 1167 case 0x33: // '3' 1168 byteValue |= (byte) 0x03; 1169 break; 1170 case 0x34: // '4' 1171 byteValue |= (byte) 0x04; 1172 break; 1173 case 0x35: // '5' 1174 byteValue |= (byte) 0x05; 1175 break; 1176 case 0x36: // '6' 1177 byteValue |= (byte) 0x06; 1178 break; 1179 case 0x37: // '7' 1180 byteValue |= (byte) 0x07; 1181 break; 1182 case 0x38: // '8' 1183 byteValue |= (byte) 0x08; 1184 break; 1185 case 0x39: // '9' 1186 byteValue |= (byte) 0x09; 1187 break; 1188 case 0x41: // 'A' 1189 case 0x61: // 'a' 1190 byteValue |= (byte) 0x0A; 1191 break; 1192 case 0x42: // 'B' 1193 case 0x62: // 'b' 1194 byteValue |= (byte) 0x0B; 1195 break; 1196 case 0x43: // 'C' 1197 case 0x63: // 'c' 1198 byteValue |= (byte) 0x0C; 1199 break; 1200 case 0x44: // 'D' 1201 case 0x64: // 'd' 1202 byteValue |= (byte) 0x0D; 1203 break; 1204 case 0x45: // 'E' 1205 case 0x65: // 'e' 1206 byteValue |= (byte) 0x0E; 1207 break; 1208 case 0x46: // 'F' 1209 case 0x66: // 'f' 1210 byteValue |= (byte) 0x0F; 1211 break; 1212 default: 1213 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1214 filterString, equalPos+i+1); 1215 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1216 } 1217 1218 buffer.appendByte(byteValue); 1219 } 1220 else 1221 { 1222 buffer.appendByte(valueBytes[i]); 1223 } 1224 } 1225 1226 subAny.add(buffer.toByteString()); 1227 buffer.clear(); 1228 } 1229 else 1230 { 1231 subAny.add(ByteString.wrap(valueBytes, firstPos+1, length)); 1232 } 1233 1234 1235 firstPos = asteriskPos; 1236 } 1237 1238 1239 // Finally, see if there is anything after the last asterisk, which would be 1240 // the subFinal value. 1241 ByteString subFinal; 1242 if (firstPos == (valueBytes.length-1)) 1243 { 1244 subFinal = null; 1245 } 1246 else 1247 { 1248 int length = valueBytes.length - firstPos - 1; 1249 1250 if (hasEscape) 1251 { 1252 ByteStringBuilder buffer = new ByteStringBuilder(length); 1253 for (int i=firstPos+1; i < valueBytes.length; i++) 1254 { 1255 if (valueBytes[i] == 0x5C) 1256 { 1257 // The next two bytes must be the hex characters that comprise the 1258 // binary value. 1259 if (i + 2 >= valueBytes.length) 1260 { 1261 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1262 filterString, equalPos+i+1); 1263 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1264 } 1265 1266 byte byteValue = 0; 1267 switch (valueBytes[++i]) 1268 { 1269 case 0x30: // '0' 1270 break; 1271 case 0x31: // '1' 1272 byteValue = (byte) 0x10; 1273 break; 1274 case 0x32: // '2' 1275 byteValue = (byte) 0x20; 1276 break; 1277 case 0x33: // '3' 1278 byteValue = (byte) 0x30; 1279 break; 1280 case 0x34: // '4' 1281 byteValue = (byte) 0x40; 1282 break; 1283 case 0x35: // '5' 1284 byteValue = (byte) 0x50; 1285 break; 1286 case 0x36: // '6' 1287 byteValue = (byte) 0x60; 1288 break; 1289 case 0x37: // '7' 1290 byteValue = (byte) 0x70; 1291 break; 1292 case 0x38: // '8' 1293 byteValue = (byte) 0x80; 1294 break; 1295 case 0x39: // '9' 1296 byteValue = (byte) 0x90; 1297 break; 1298 case 0x41: // 'A' 1299 case 0x61: // 'a' 1300 byteValue = (byte) 0xA0; 1301 break; 1302 case 0x42: // 'B' 1303 case 0x62: // 'b' 1304 byteValue = (byte) 0xB0; 1305 break; 1306 case 0x43: // 'C' 1307 case 0x63: // 'c' 1308 byteValue = (byte) 0xC0; 1309 break; 1310 case 0x44: // 'D' 1311 case 0x64: // 'd' 1312 byteValue = (byte) 0xD0; 1313 break; 1314 case 0x45: // 'E' 1315 case 0x65: // 'e' 1316 byteValue = (byte) 0xE0; 1317 break; 1318 case 0x46: // 'F' 1319 case 0x66: // 'f' 1320 byteValue = (byte) 0xF0; 1321 break; 1322 default: 1323 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1324 filterString, equalPos+i+1); 1325 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1326 } 1327 1328 switch (valueBytes[++i]) 1329 { 1330 case 0x30: // '0' 1331 break; 1332 case 0x31: // '1' 1333 byteValue |= (byte) 0x01; 1334 break; 1335 case 0x32: // '2' 1336 byteValue |= (byte) 0x02; 1337 break; 1338 case 0x33: // '3' 1339 byteValue |= (byte) 0x03; 1340 break; 1341 case 0x34: // '4' 1342 byteValue |= (byte) 0x04; 1343 break; 1344 case 0x35: // '5' 1345 byteValue |= (byte) 0x05; 1346 break; 1347 case 0x36: // '6' 1348 byteValue |= (byte) 0x06; 1349 break; 1350 case 0x37: // '7' 1351 byteValue |= (byte) 0x07; 1352 break; 1353 case 0x38: // '8' 1354 byteValue |= (byte) 0x08; 1355 break; 1356 case 0x39: // '9' 1357 byteValue |= (byte) 0x09; 1358 break; 1359 case 0x41: // 'A' 1360 case 0x61: // 'a' 1361 byteValue |= (byte) 0x0A; 1362 break; 1363 case 0x42: // 'B' 1364 case 0x62: // 'b' 1365 byteValue |= (byte) 0x0B; 1366 break; 1367 case 0x43: // 'C' 1368 case 0x63: // 'c' 1369 byteValue |= (byte) 0x0C; 1370 break; 1371 case 0x44: // 'D' 1372 case 0x64: // 'd' 1373 byteValue |= (byte) 0x0D; 1374 break; 1375 case 0x45: // 'E' 1376 case 0x65: // 'e' 1377 byteValue |= (byte) 0x0E; 1378 break; 1379 case 0x46: // 'F' 1380 case 0x66: // 'f' 1381 byteValue |= (byte) 0x0F; 1382 break; 1383 default: 1384 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1385 filterString, equalPos+i+1); 1386 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1387 } 1388 1389 buffer.appendByte(byteValue); 1390 } 1391 else 1392 { 1393 buffer.appendByte(valueBytes[i]); 1394 } 1395 } 1396 1397 subFinal = buffer.toByteString(); 1398 } 1399 else 1400 { 1401 subFinal = ByteString.wrap(valueBytes, firstPos+1, length); 1402 } 1403 } 1404 1405 1406 return new LDAPFilter(FilterType.SUBSTRING, null, null, attrType, null, 1407 subInitial, subAny, subFinal, null, false); 1408 } 1409 1410 1411 1412 /** 1413 * Decodes an extensible match filter component based on the provided 1414 * information. 1415 * 1416 * @param filterString The filter string containing the information to 1417 * decode. 1418 * @param startPos The position in the filter string of the first 1419 * character in the extensible match filter. 1420 * @param equalPos The position of the equal sign in the extensible 1421 * match filter. 1422 * @param endPos The position of the first character after the end of 1423 * the extensible match filter. 1424 * 1425 * @return The decoded LDAP filter. 1426 * 1427 * @throws LDAPException If a problem occurs while attempting to decode the 1428 * extensible match filter. 1429 */ 1430 private static LDAPFilter decodeExtensibleMatchFilter(String filterString, 1431 int startPos, 1432 int equalPos, 1433 int endPos) 1434 throws LDAPException 1435 { 1436 String attributeType = null; 1437 boolean dnAttributes = false; 1438 String matchingRuleID = null; 1439 1440 1441 // Look at the first character. If it is a colon, then it must be followed 1442 // by either the string "dn" or the matching rule ID. If it is not, then 1443 // must be the attribute type. 1444 String lowerLeftStr = 1445 toLowerCase(filterString.substring(startPos, equalPos)); 1446 if (filterString.charAt(startPos) == ':') 1447 { 1448 // See if it starts with ":dn". Otherwise, it much be the matching rule 1449 // ID. 1450 if (lowerLeftStr.startsWith(":dn:")) 1451 { 1452 dnAttributes = true; 1453 1454 if(startPos+4 < equalPos-1) 1455 { 1456 matchingRuleID = filterString.substring(startPos+4, equalPos-1); 1457 } 1458 } 1459 else 1460 { 1461 matchingRuleID = filterString.substring(startPos+1, equalPos-1); 1462 } 1463 } 1464 else 1465 { 1466 int colonPos = filterString.indexOf(':',startPos); 1467 if (colonPos < 0) 1468 { 1469 LocalizableMessage message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_COLON.get( 1470 filterString, startPos); 1471 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1472 } 1473 1474 1475 attributeType = filterString.substring(startPos, colonPos); 1476 1477 1478 // If there is anything left, then it should be ":dn" and/or ":" followed 1479 // by the matching rule ID. 1480 if (colonPos < equalPos-1) 1481 { 1482 if (lowerLeftStr.startsWith(":dn:", colonPos - startPos)) 1483 { 1484 dnAttributes = true; 1485 1486 if (colonPos+4 < equalPos-1) 1487 { 1488 matchingRuleID = filterString.substring(colonPos+4, equalPos-1); 1489 } 1490 } 1491 else 1492 { 1493 matchingRuleID = filterString.substring(colonPos+1, equalPos-1); 1494 } 1495 } 1496 } 1497 1498 1499 // Parse out the attribute value. 1500 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos)); 1501 boolean hasEscape = false; 1502 for (byte valueByte : valueBytes) 1503 { 1504 if (valueByte == 0x5C) 1505 { 1506 hasEscape = true; 1507 break; 1508 } 1509 } 1510 1511 ByteString value; 1512 if (hasEscape) 1513 { 1514 ByteStringBuilder valueBuffer = new ByteStringBuilder(valueBytes.length); 1515 for (int i=0; i < valueBytes.length; i++) 1516 { 1517 if (valueBytes[i] == 0x5C) // The backslash character 1518 { 1519 // The next two bytes must be the hex characters that comprise the 1520 // binary value. 1521 if (i + 2 >= valueBytes.length) 1522 { 1523 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1524 filterString, equalPos+i+1); 1525 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1526 } 1527 1528 byte byteValue = 0; 1529 switch (valueBytes[++i]) 1530 { 1531 case 0x30: // '0' 1532 break; 1533 case 0x31: // '1' 1534 byteValue = (byte) 0x10; 1535 break; 1536 case 0x32: // '2' 1537 byteValue = (byte) 0x20; 1538 break; 1539 case 0x33: // '3' 1540 byteValue = (byte) 0x30; 1541 break; 1542 case 0x34: // '4' 1543 byteValue = (byte) 0x40; 1544 break; 1545 case 0x35: // '5' 1546 byteValue = (byte) 0x50; 1547 break; 1548 case 0x36: // '6' 1549 byteValue = (byte) 0x60; 1550 break; 1551 case 0x37: // '7' 1552 byteValue = (byte) 0x70; 1553 break; 1554 case 0x38: // '8' 1555 byteValue = (byte) 0x80; 1556 break; 1557 case 0x39: // '9' 1558 byteValue = (byte) 0x90; 1559 break; 1560 case 0x41: // 'A' 1561 case 0x61: // 'a' 1562 byteValue = (byte) 0xA0; 1563 break; 1564 case 0x42: // 'B' 1565 case 0x62: // 'b' 1566 byteValue = (byte) 0xB0; 1567 break; 1568 case 0x43: // 'C' 1569 case 0x63: // 'c' 1570 byteValue = (byte) 0xC0; 1571 break; 1572 case 0x44: // 'D' 1573 case 0x64: // 'd' 1574 byteValue = (byte) 0xD0; 1575 break; 1576 case 0x45: // 'E' 1577 case 0x65: // 'e' 1578 byteValue = (byte) 0xE0; 1579 break; 1580 case 0x46: // 'F' 1581 case 0x66: // 'f' 1582 byteValue = (byte) 0xF0; 1583 break; 1584 default: 1585 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1586 filterString, equalPos+i+1); 1587 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1588 } 1589 1590 switch (valueBytes[++i]) 1591 { 1592 case 0x30: // '0' 1593 break; 1594 case 0x31: // '1' 1595 byteValue |= (byte) 0x01; 1596 break; 1597 case 0x32: // '2' 1598 byteValue |= (byte) 0x02; 1599 break; 1600 case 0x33: // '3' 1601 byteValue |= (byte) 0x03; 1602 break; 1603 case 0x34: // '4' 1604 byteValue |= (byte) 0x04; 1605 break; 1606 case 0x35: // '5' 1607 byteValue |= (byte) 0x05; 1608 break; 1609 case 0x36: // '6' 1610 byteValue |= (byte) 0x06; 1611 break; 1612 case 0x37: // '7' 1613 byteValue |= (byte) 0x07; 1614 break; 1615 case 0x38: // '8' 1616 byteValue |= (byte) 0x08; 1617 break; 1618 case 0x39: // '9' 1619 byteValue |= (byte) 0x09; 1620 break; 1621 case 0x41: // 'A' 1622 case 0x61: // 'a' 1623 byteValue |= (byte) 0x0A; 1624 break; 1625 case 0x42: // 'B' 1626 case 0x62: // 'b' 1627 byteValue |= (byte) 0x0B; 1628 break; 1629 case 0x43: // 'C' 1630 case 0x63: // 'c' 1631 byteValue |= (byte) 0x0C; 1632 break; 1633 case 0x44: // 'D' 1634 case 0x64: // 'd' 1635 byteValue |= (byte) 0x0D; 1636 break; 1637 case 0x45: // 'E' 1638 case 0x65: // 'e' 1639 byteValue |= (byte) 0x0E; 1640 break; 1641 case 0x46: // 'F' 1642 case 0x66: // 'f' 1643 byteValue |= (byte) 0x0F; 1644 break; 1645 default: 1646 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1647 filterString, equalPos+i+1); 1648 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1649 } 1650 1651 valueBuffer.appendByte(byteValue); 1652 } 1653 else 1654 { 1655 valueBuffer.appendByte(valueBytes[i]); 1656 } 1657 } 1658 1659 value = valueBuffer.toByteString(); 1660 } 1661 else 1662 { 1663 value = ByteString.wrap(valueBytes); 1664 } 1665 1666 1667 // Make sure that the filter has at least one of an attribute description 1668 // and/or a matching rule ID. 1669 if (attributeType == null && matchingRuleID == null) 1670 { 1671 LocalizableMessage message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR.get( 1672 filterString, startPos); 1673 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1674 } 1675 1676 1677 return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null, 1678 attributeType, value, null, null, null, 1679 matchingRuleID, dnAttributes); 1680 } 1681 1682 1683 1684 /** 1685 * Retrieves the filter type for this search filter. 1686 * 1687 * @return The filter type for this search filter. 1688 */ 1689 @Override 1690 public FilterType getFilterType() 1691 { 1692 return filterType; 1693 } 1694 1695 1696 1697 /** 1698 * Retrieves the set of subordinate filter components for AND or OR searches. 1699 * The contents of the returned list may be altered by the caller. 1700 * 1701 * @return The set of subordinate filter components for AND and OR searches, 1702 * or <CODE>null</CODE> if this is not an AND or OR search. 1703 */ 1704 @Override 1705 public ArrayList<RawFilter> getFilterComponents() 1706 { 1707 return filterComponents; 1708 } 1709 1710 1711 1712 /** 1713 * Retrieves the subordinate filter component for NOT searches. 1714 * 1715 * @return The subordinate filter component for NOT searches, or 1716 * <CODE>null</CODE> if this is not a NOT search. 1717 */ 1718 @Override 1719 public RawFilter getNOTComponent() 1720 { 1721 return notComponent; 1722 } 1723 1724 1725 1726 /** 1727 * Retrieves the attribute type for this search filter. This will not be 1728 * applicable for AND, OR, or NOT filters. 1729 * 1730 * @return The attribute type for this search filter, or <CODE>null</CODE> if 1731 * there is none. 1732 */ 1733 @Override 1734 public String getAttributeType() 1735 { 1736 return attributeType; 1737 } 1738 1739 1740 1741 /** 1742 * Retrieves the assertion value for this search filter. This will only be 1743 * applicable for equality, greater or equal, less or equal, approximate, or 1744 * extensible matching filters. 1745 * 1746 * @return The assertion value for this search filter, or <CODE>null</CODE> 1747 * if there is none. 1748 */ 1749 @Override 1750 public ByteString getAssertionValue() 1751 { 1752 return assertionValue; 1753 } 1754 1755 1756 1757 /** 1758 * Retrieves the subInitial component for this substring filter. This is only 1759 * applicable for substring search filters, but even substring filters might 1760 * not have a value for this component. 1761 * 1762 * @return The subInitial component for this substring filter, or 1763 * <CODE>null</CODE> if there is none. 1764 */ 1765 @Override 1766 public ByteString getSubInitialElement() 1767 { 1768 return subInitialElement; 1769 } 1770 1771 1772 1773 /** 1774 * Specifies the subInitial element for this substring filter. This will be 1775 * ignored for all other types of filters. 1776 * 1777 * @param subInitialElement The subInitial element for this substring 1778 * filter. 1779 */ 1780 public void setSubInitialElement(ByteString subInitialElement) 1781 { 1782 this.subInitialElement = subInitialElement; 1783 } 1784 1785 1786 1787 /** 1788 * Retrieves the set of subAny elements for this substring filter. This is 1789 * only applicable for substring search filters, and even then may be null or 1790 * empty for some substring filters. 1791 * 1792 * @return The set of subAny elements for this substring filter, or 1793 * <CODE>null</CODE> if there are none. 1794 */ 1795 @Override 1796 public ArrayList<ByteString> getSubAnyElements() 1797 { 1798 return subAnyElements; 1799 } 1800 1801 1802 1803 /** 1804 * Retrieves the subFinal element for this substring filter. This is not 1805 * applicable for any other filter type, and may not be provided even for some 1806 * substring filters. 1807 * 1808 * @return The subFinal element for this substring filter, or 1809 * <CODE>null</CODE> if there is none. 1810 */ 1811 @Override 1812 public ByteString getSubFinalElement() 1813 { 1814 return subFinalElement; 1815 } 1816 1817 1818 1819 /** 1820 * Retrieves the matching rule ID for this extensible match filter. This is 1821 * not applicable for any other type of filter and may not be included in 1822 * some extensible matching filters. 1823 * 1824 * @return The matching rule ID for this extensible match filter, or 1825 * <CODE>null</CODE> if there is none. 1826 */ 1827 @Override 1828 public String getMatchingRuleID() 1829 { 1830 return matchingRuleID; 1831 } 1832 1833 1834 1835 /** 1836 * Retrieves the value of the DN attributes flag for this extensible match 1837 * filter, which indicates whether to perform matching on the components of 1838 * the DN. This does not apply for any other type of filter. 1839 * 1840 * @return The value of the DN attributes flag for this extensibleMatch 1841 * filter. 1842 */ 1843 @Override 1844 public boolean getDNAttributes() 1845 { 1846 return dnAttributes; 1847 } 1848 1849 1850 1851 /** 1852 * Converts this LDAP filter to a search filter that may be used by the 1853 * Directory Server's core processing. 1854 * 1855 * @return The generated search filter. 1856 * 1857 * @throws DirectoryException If a problem occurs while attempting to 1858 * construct the search filter. 1859 */ 1860 @Override 1861 public SearchFilter toSearchFilter() 1862 throws DirectoryException 1863 { 1864 ArrayList<SearchFilter> subComps; 1865 if (filterComponents == null) 1866 { 1867 subComps = null; 1868 } 1869 else 1870 { 1871 subComps = new ArrayList<>(filterComponents.size()); 1872 for (RawFilter f : filterComponents) 1873 { 1874 subComps.add(f.toSearchFilter()); 1875 } 1876 } 1877 1878 1879 SearchFilter notComp; 1880 if (notComponent == null) 1881 { 1882 notComp = null; 1883 } 1884 else 1885 { 1886 notComp = notComponent.toSearchFilter(); 1887 } 1888 1889 1890 AttributeType attrType; 1891 HashSet<String> options; 1892 if (attributeType == null) 1893 { 1894 attrType = null; 1895 options = null; 1896 } 1897 else 1898 { 1899 int semicolonPos = attributeType.indexOf(';'); 1900 if (semicolonPos > 0) 1901 { 1902 String baseName = attributeType.substring(0, semicolonPos); 1903 attrType = DirectoryServer.getAttributeType(baseName); 1904 options = new HashSet<>(); 1905 StringTokenizer tokenizer = 1906 new StringTokenizer(attributeType.substring(semicolonPos+1), ";"); 1907 while (tokenizer.hasMoreTokens()) 1908 { 1909 options.add(tokenizer.nextToken()); 1910 } 1911 } 1912 else 1913 { 1914 options = null; 1915 attrType = DirectoryServer.getAttributeType(attributeType); 1916 } 1917 } 1918 1919 1920 if (assertionValue != null && attrType == null) 1921 { 1922 if (matchingRuleID == null) 1923 { 1924 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1925 ERR_LDAP_FILTER_VALUE_WITH_NO_ATTR_OR_MR.get()); 1926 } 1927 1928 MatchingRule mr = DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID)); 1929 if (mr == null) 1930 { 1931 throw new DirectoryException(ResultCode.INAPPROPRIATE_MATCHING, 1932 ERR_LDAP_FILTER_UNKNOWN_MATCHING_RULE.get(matchingRuleID)); 1933 } 1934 } 1935 1936 ArrayList<ByteString> subAnyComps = 1937 subAnyElements != null ? new ArrayList<ByteString>(subAnyElements) : null; 1938 1939 return new SearchFilter(filterType, subComps, notComp, attrType, 1940 options, assertionValue, subInitialElement, subAnyComps, 1941 subFinalElement, matchingRuleID, dnAttributes); 1942 } 1943 1944 1945 1946 /** 1947 * Appends a string representation of this search filter to the provided 1948 * buffer. 1949 * 1950 * @param buffer The buffer to which the information should be appended. 1951 */ 1952 @Override 1953 public void toString(StringBuilder buffer) 1954 { 1955 switch (filterType) 1956 { 1957 case AND: 1958 buffer.append("(&"); 1959 for (RawFilter f : filterComponents) 1960 { 1961 f.toString(buffer); 1962 } 1963 buffer.append(")"); 1964 break; 1965 case OR: 1966 buffer.append("(|"); 1967 for (RawFilter f : filterComponents) 1968 { 1969 f.toString(buffer); 1970 } 1971 buffer.append(")"); 1972 break; 1973 case NOT: 1974 buffer.append("(!"); 1975 notComponent.toString(buffer); 1976 buffer.append(")"); 1977 break; 1978 case EQUALITY: 1979 buffer.append("("); 1980 buffer.append(attributeType); 1981 buffer.append("="); 1982 valueToFilterString(buffer, assertionValue); 1983 buffer.append(")"); 1984 break; 1985 case SUBSTRING: 1986 buffer.append("("); 1987 buffer.append(attributeType); 1988 buffer.append("="); 1989 1990 if (subInitialElement != null) 1991 { 1992 valueToFilterString(buffer, subInitialElement); 1993 } 1994 1995 if (subAnyElements != null && !subAnyElements.isEmpty()) 1996 { 1997 for (ByteString s : subAnyElements) 1998 { 1999 buffer.append("*"); 2000 valueToFilterString(buffer, s); 2001 } 2002 } 2003 2004 buffer.append("*"); 2005 2006 if (subFinalElement != null) 2007 { 2008 valueToFilterString(buffer, subFinalElement); 2009 } 2010 2011 buffer.append(")"); 2012 break; 2013 case GREATER_OR_EQUAL: 2014 buffer.append("("); 2015 buffer.append(attributeType); 2016 buffer.append(">="); 2017 valueToFilterString(buffer, assertionValue); 2018 buffer.append(")"); 2019 break; 2020 case LESS_OR_EQUAL: 2021 buffer.append("("); 2022 buffer.append(attributeType); 2023 buffer.append("<="); 2024 valueToFilterString(buffer, assertionValue); 2025 buffer.append(")"); 2026 break; 2027 case PRESENT: 2028 buffer.append("("); 2029 buffer.append(attributeType); 2030 buffer.append("=*)"); 2031 break; 2032 case APPROXIMATE_MATCH: 2033 buffer.append("("); 2034 buffer.append(attributeType); 2035 buffer.append("~="); 2036 valueToFilterString(buffer, assertionValue); 2037 buffer.append(")"); 2038 break; 2039 case EXTENSIBLE_MATCH: 2040 buffer.append("("); 2041 2042 if (attributeType != null) 2043 { 2044 buffer.append(attributeType); 2045 } 2046 2047 if (dnAttributes) 2048 { 2049 buffer.append(":dn"); 2050 } 2051 2052 if (matchingRuleID != null) 2053 { 2054 buffer.append(":"); 2055 buffer.append(matchingRuleID); 2056 } 2057 2058 buffer.append(":="); 2059 valueToFilterString(buffer, assertionValue); 2060 buffer.append(")"); 2061 break; 2062 } 2063 } 2064 2065 /** 2066 * Returns the {@code objectClass} presence filter {@code (objectClass=*)}. 2067 * 2068 * @return The {@code objectClass} presence filter {@code (objectClass=*)}. 2069 */ 2070 public static LDAPFilter objectClassPresent() 2071 { 2072 if (objectClassPresent == null) 2073 { 2074 try 2075 { 2076 objectClassPresent = LDAPFilter.decode("(objectclass=*)"); 2077 } 2078 catch (LDAPException canNeverHappen) 2079 { 2080 logger.traceException(canNeverHappen); 2081 } 2082 } 2083 return objectClassPresent; 2084 } 2085}