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.LinkedList; 022import java.util.List; 023 024import org.forgerock.i18n.LocalizableMessage; 025import org.forgerock.i18n.LocalizedIllegalArgumentException; 026import org.forgerock.i18n.slf4j.LocalizedLogger; 027import org.forgerock.opendj.ldap.AttributeDescription; 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.UnknownSchemaElementException; 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 description for several filter types. */ 080 private String attributeDescription; 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 attributeDescription The attribute description 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 attributeDescription, 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.attributeDescription = attributeDescription; 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 attributeDescription = 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 attributeDescription = 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 attributeDescription = 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 attributeDescription = 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 attributeDescription = 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 attributeDescription = null; 242 } 243 else 244 { 245 attributeDescription = 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 attribute description. 427 // Make sure that the characters it contains are acceptable for attribute descriptions, 428 // including those allowed by attribute name exceptions 429 // (ASCII letters and digits, the dash, and the underscore). 430 // We also need to allow attribute options, which includes the semicolon and the equal sign. 431 String attrType = filterString.substring(startPos, attrEndPos); 432 for (int i=0; i < attrType.length(); i++) 433 { 434 switch (attrType.charAt(i)) 435 { 436 case '-': 437 case '0': 438 case '1': 439 case '2': 440 case '3': 441 case '4': 442 case '5': 443 case '6': 444 case '7': 445 case '8': 446 case '9': 447 case ';': 448 case '=': 449 case 'A': 450 case 'B': 451 case 'C': 452 case 'D': 453 case 'E': 454 case 'F': 455 case 'G': 456 case 'H': 457 case 'I': 458 case 'J': 459 case 'K': 460 case 'L': 461 case 'M': 462 case 'N': 463 case 'O': 464 case 'P': 465 case 'Q': 466 case 'R': 467 case 'S': 468 case 'T': 469 case 'U': 470 case 'V': 471 case 'W': 472 case 'X': 473 case 'Y': 474 case 'Z': 475 case '_': 476 case 'a': 477 case 'b': 478 case 'c': 479 case 'd': 480 case 'e': 481 case 'f': 482 case 'g': 483 case 'h': 484 case 'i': 485 case 'j': 486 case 'k': 487 case 'l': 488 case 'm': 489 case 'n': 490 case 'o': 491 case 'p': 492 case 'q': 493 case 'r': 494 case 's': 495 case 't': 496 case 'u': 497 case 'v': 498 case 'w': 499 case 'x': 500 case 'y': 501 case 'z': 502 // These are all OK. 503 break; 504 505 case '.': 506 case '/': 507 case ':': 508 case '<': 509 case '>': 510 case '?': 511 case '@': 512 case '[': 513 case '\\': 514 case ']': 515 case '^': 516 case '`': 517 // These are not allowed, but they are explicitly called out because 518 // they are included in the range of values between '-' and 'z', and 519 // making sure all possible characters are included can help make the 520 // switch statement more efficient. We'll fall through to the default 521 // clause to reject them. 522 default: 523 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_CHAR_IN_ATTR_TYPE.get( 524 attrType, attrType.charAt(i), i); 525 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 526 } 527 } 528 529 530 // Get the attribute value. 531 String valueStr = filterString.substring(equalPos+1, endPos); 532 if (valueStr.length() == 0) 533 { 534 return new LDAPFilter(filterType, null, null, attrType, 535 ByteString.empty(), null, null, null, null, 536 false); 537 } 538 else if (valueStr.equals("*")) 539 { 540 return new LDAPFilter(FilterType.PRESENT, null, null, attrType, null, 541 null, null, null, null, false); 542 } 543 else if (valueStr.indexOf('*') >= 0) 544 { 545 return decodeSubstringFilter(filterString, attrType, equalPos, endPos); 546 } 547 else 548 { 549 boolean hasEscape = false; 550 byte[] valueBytes = getBytes(valueStr); 551 for (byte valueByte : valueBytes) 552 { 553 if (valueByte == 0x5C) // The backslash character 554 { 555 hasEscape = true; 556 break; 557 } 558 } 559 560 ByteString value; 561 if (hasEscape) 562 { 563 ByteStringBuilder valueBuffer = 564 new ByteStringBuilder(valueStr.length()); 565 for (int i=0; i < valueBytes.length; i++) 566 { 567 if (valueBytes[i] == 0x5C) // The backslash character 568 { 569 // The next two bytes must be the hex characters that comprise the 570 // binary value. 571 if (i + 2 >= valueBytes.length) 572 { 573 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 574 filterString, equalPos+i+1); 575 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 576 } 577 578 byte byteValue = 0; 579 switch (valueBytes[++i]) 580 { 581 case 0x30: // '0' 582 break; 583 case 0x31: // '1' 584 byteValue = (byte) 0x10; 585 break; 586 case 0x32: // '2' 587 byteValue = (byte) 0x20; 588 break; 589 case 0x33: // '3' 590 byteValue = (byte) 0x30; 591 break; 592 case 0x34: // '4' 593 byteValue = (byte) 0x40; 594 break; 595 case 0x35: // '5' 596 byteValue = (byte) 0x50; 597 break; 598 case 0x36: // '6' 599 byteValue = (byte) 0x60; 600 break; 601 case 0x37: // '7' 602 byteValue = (byte) 0x70; 603 break; 604 case 0x38: // '8' 605 byteValue = (byte) 0x80; 606 break; 607 case 0x39: // '9' 608 byteValue = (byte) 0x90; 609 break; 610 case 0x41: // 'A' 611 case 0x61: // 'a' 612 byteValue = (byte) 0xA0; 613 break; 614 case 0x42: // 'B' 615 case 0x62: // 'b' 616 byteValue = (byte) 0xB0; 617 break; 618 case 0x43: // 'C' 619 case 0x63: // 'c' 620 byteValue = (byte) 0xC0; 621 break; 622 case 0x44: // 'D' 623 case 0x64: // 'd' 624 byteValue = (byte) 0xD0; 625 break; 626 case 0x45: // 'E' 627 case 0x65: // 'e' 628 byteValue = (byte) 0xE0; 629 break; 630 case 0x46: // 'F' 631 case 0x66: // 'f' 632 byteValue = (byte) 0xF0; 633 break; 634 default: 635 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 636 filterString, equalPos+i+1); 637 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 638 } 639 640 switch (valueBytes[++i]) 641 { 642 case 0x30: // '0' 643 break; 644 case 0x31: // '1' 645 byteValue |= (byte) 0x01; 646 break; 647 case 0x32: // '2' 648 byteValue |= (byte) 0x02; 649 break; 650 case 0x33: // '3' 651 byteValue |= (byte) 0x03; 652 break; 653 case 0x34: // '4' 654 byteValue |= (byte) 0x04; 655 break; 656 case 0x35: // '5' 657 byteValue |= (byte) 0x05; 658 break; 659 case 0x36: // '6' 660 byteValue |= (byte) 0x06; 661 break; 662 case 0x37: // '7' 663 byteValue |= (byte) 0x07; 664 break; 665 case 0x38: // '8' 666 byteValue |= (byte) 0x08; 667 break; 668 case 0x39: // '9' 669 byteValue |= (byte) 0x09; 670 break; 671 case 0x41: // 'A' 672 case 0x61: // 'a' 673 byteValue |= (byte) 0x0A; 674 break; 675 case 0x42: // 'B' 676 case 0x62: // 'b' 677 byteValue |= (byte) 0x0B; 678 break; 679 case 0x43: // 'C' 680 case 0x63: // 'c' 681 byteValue |= (byte) 0x0C; 682 break; 683 case 0x44: // 'D' 684 case 0x64: // 'd' 685 byteValue |= (byte) 0x0D; 686 break; 687 case 0x45: // 'E' 688 case 0x65: // 'e' 689 byteValue |= (byte) 0x0E; 690 break; 691 case 0x46: // 'F' 692 case 0x66: // 'f' 693 byteValue |= (byte) 0x0F; 694 break; 695 default: 696 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 697 filterString, equalPos+i+1); 698 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 699 } 700 701 valueBuffer.appendByte(byteValue); 702 } 703 else 704 { 705 valueBuffer.appendByte(valueBytes[i]); 706 } 707 } 708 709 value = valueBuffer.toByteString(); 710 } 711 else 712 { 713 value = ByteString.wrap(valueBytes); 714 } 715 716 return new LDAPFilter(filterType, null, null, attrType, value, null, null, 717 null, null, false); 718 } 719 } 720 721 722 723 /** 724 * Decodes a set of filters from the provided filter string within the 725 * indicated range. 726 * 727 * @param filterType The filter type for this compound filter. It must be 728 * an AND, OR or NOT filter. 729 * @param filterString The string containing the filter information to 730 * decode. 731 * @param startPos The position of the first character in the set of 732 * filters to decode. 733 * @param endPos The position of the first character after the end of 734 * the set of filters to decode. 735 * 736 * @return The decoded LDAP filter. 737 * 738 * @throws LDAPException If a problem occurs while attempting to decode the 739 * compound filter. 740 */ 741 private static LDAPFilter decodeCompoundFilter(FilterType filterType, 742 String filterString, 743 int startPos, int endPos) 744 throws LDAPException 745 { 746 // Create a list to hold the returned components. 747 ArrayList<RawFilter> filterComponents = new ArrayList<>(); 748 749 750 // If the end pos is equal to the start pos, then there are no components. 751 if (startPos == endPos) 752 { 753 if (filterType == FilterType.NOT) 754 { 755 LocalizableMessage message = 756 ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos); 757 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 758 } 759 else 760 { 761 // This is valid and will be treated as a TRUE/FALSE filter. 762 return new LDAPFilter(filterType, filterComponents, null, null, null, 763 null, null, null, null, false); 764 } 765 } 766 767 768 // The first and last characters must be parentheses. If not, then that's 769 // an error. 770 if (filterString.charAt(startPos) != '(' || 771 filterString.charAt(endPos-1) != ')') 772 { 773 LocalizableMessage message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get( 774 filterString, startPos, endPos); 775 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 776 } 777 778 779 // Iterate through the characters in the value. Whenever an open 780 // parenthesis is found, locate the corresponding close parenthesis by 781 // counting the number of intermediate open/close parentheses. 782 int pendingOpens = 0; 783 int openPos = -1; 784 for (int i=startPos; i < endPos; i++) 785 { 786 char c = filterString.charAt(i); 787 if (c == '(') 788 { 789 if (openPos < 0) 790 { 791 openPos = i; 792 } 793 794 pendingOpens++; 795 } 796 else if (c == ')') 797 { 798 pendingOpens--; 799 if (pendingOpens == 0) 800 { 801 filterComponents.add(decode(filterString, openPos, i+1)); 802 openPos = -1; 803 } 804 else if (pendingOpens < 0) 805 { 806 LocalizableMessage message = ERR_LDAP_FILTER_NO_CORRESPONDING_OPEN_PARENTHESIS. 807 get(filterString, i); 808 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 809 } 810 } 811 else if (pendingOpens <= 0) 812 { 813 LocalizableMessage message = ERR_LDAP_FILTER_COMPOUND_MISSING_PARENTHESES.get( 814 filterString, startPos, endPos); 815 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 816 } 817 } 818 819 820 // At this point, we have parsed the entire set of filter components. The 821 // list of open parenthesis positions must be empty. 822 if (pendingOpens != 0) 823 { 824 LocalizableMessage message = ERR_LDAP_FILTER_NO_CORRESPONDING_CLOSE_PARENTHESIS.get( 825 filterString, openPos); 826 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 827 } 828 829 830 // We should have everything we need, so return the list. 831 if (filterType == FilterType.NOT) 832 { 833 if (filterComponents.size() != 1) 834 { 835 LocalizableMessage message = 836 ERR_LDAP_FILTER_NOT_EXACTLY_ONE.get(filterString, startPos, endPos); 837 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 838 } 839 RawFilter notComponent = filterComponents.get(0); 840 return new LDAPFilter(filterType, null, notComponent, null, null, 841 null, null, null, null, false); 842 } 843 else 844 { 845 return new LDAPFilter(filterType, filterComponents, null, null, null, 846 null, null, null, null, false); 847 } 848 } 849 850 851 852 /** 853 * Decodes a substring search filter component based on the provided 854 * information. 855 * 856 * @param filterString The filter string containing the information to 857 * decode. 858 * @param attrDesc The attribute description for this substring filter 859 * component. 860 * @param equalPos The location of the equal sign separating the 861 * attribute description from the value. 862 * @param endPos The position of the first character after the end of 863 * the substring value. 864 * 865 * @return The decoded LDAP filter. 866 * 867 * @throws LDAPException If a problem occurs while attempting to decode the 868 * substring filter. 869 */ 870 private static LDAPFilter decodeSubstringFilter(String filterString, 871 String attrDesc, int equalPos, 872 int endPos) 873 throws LDAPException 874 { 875 // Get a binary representation of the value. 876 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos)); 877 878 879 // Find the locations of all the asterisks in the value. Also, check to 880 // see if there are any escaped values, since they will need special 881 // treatment. 882 boolean hasEscape = false; 883 LinkedList<Integer> asteriskPositions = new LinkedList<>(); 884 for (int i=0; i < valueBytes.length; i++) 885 { 886 if (valueBytes[i] == 0x2A) // The asterisk. 887 { 888 asteriskPositions.add(i); 889 } 890 else if (valueBytes[i] == 0x5C) // The backslash. 891 { 892 hasEscape = true; 893 } 894 } 895 896 897 // If there were no asterisks, then this isn't a substring filter. 898 if (asteriskPositions.isEmpty()) 899 { 900 LocalizableMessage message = ERR_LDAP_FILTER_SUBSTRING_NO_ASTERISKS.get( 901 filterString, equalPos+1, endPos); 902 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 903 } 904 905 906 // If the value starts with an asterisk, then there is no subInitial 907 // component. Otherwise, parse out the subInitial. 908 ByteString subInitial; 909 int firstPos = asteriskPositions.removeFirst(); 910 if (firstPos == 0) 911 { 912 subInitial = null; 913 } 914 else 915 { 916 if (hasEscape) 917 { 918 ByteStringBuilder buffer = new ByteStringBuilder(firstPos); 919 for (int i=0; i < firstPos; i++) 920 { 921 if (valueBytes[i] == 0x5C) 922 { 923 // The next two bytes must be the hex characters that comprise the 924 // binary value. 925 if (i + 2 >= valueBytes.length) 926 { 927 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 928 filterString, equalPos+i+1); 929 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 930 } 931 932 byte byteValue = 0; 933 switch (valueBytes[++i]) 934 { 935 case 0x30: // '0' 936 break; 937 case 0x31: // '1' 938 byteValue = (byte) 0x10; 939 break; 940 case 0x32: // '2' 941 byteValue = (byte) 0x20; 942 break; 943 case 0x33: // '3' 944 byteValue = (byte) 0x30; 945 break; 946 case 0x34: // '4' 947 byteValue = (byte) 0x40; 948 break; 949 case 0x35: // '5' 950 byteValue = (byte) 0x50; 951 break; 952 case 0x36: // '6' 953 byteValue = (byte) 0x60; 954 break; 955 case 0x37: // '7' 956 byteValue = (byte) 0x70; 957 break; 958 case 0x38: // '8' 959 byteValue = (byte) 0x80; 960 break; 961 case 0x39: // '9' 962 byteValue = (byte) 0x90; 963 break; 964 case 0x41: // 'A' 965 case 0x61: // 'a' 966 byteValue = (byte) 0xA0; 967 break; 968 case 0x42: // 'B' 969 case 0x62: // 'b' 970 byteValue = (byte) 0xB0; 971 break; 972 case 0x43: // 'C' 973 case 0x63: // 'c' 974 byteValue = (byte) 0xC0; 975 break; 976 case 0x44: // 'D' 977 case 0x64: // 'd' 978 byteValue = (byte) 0xD0; 979 break; 980 case 0x45: // 'E' 981 case 0x65: // 'e' 982 byteValue = (byte) 0xE0; 983 break; 984 case 0x46: // 'F' 985 case 0x66: // 'f' 986 byteValue = (byte) 0xF0; 987 break; 988 default: 989 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 990 filterString, equalPos+i+1); 991 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 992 } 993 994 switch (valueBytes[++i]) 995 { 996 case 0x30: // '0' 997 break; 998 case 0x31: // '1' 999 byteValue |= (byte) 0x01; 1000 break; 1001 case 0x32: // '2' 1002 byteValue |= (byte) 0x02; 1003 break; 1004 case 0x33: // '3' 1005 byteValue |= (byte) 0x03; 1006 break; 1007 case 0x34: // '4' 1008 byteValue |= (byte) 0x04; 1009 break; 1010 case 0x35: // '5' 1011 byteValue |= (byte) 0x05; 1012 break; 1013 case 0x36: // '6' 1014 byteValue |= (byte) 0x06; 1015 break; 1016 case 0x37: // '7' 1017 byteValue |= (byte) 0x07; 1018 break; 1019 case 0x38: // '8' 1020 byteValue |= (byte) 0x08; 1021 break; 1022 case 0x39: // '9' 1023 byteValue |= (byte) 0x09; 1024 break; 1025 case 0x41: // 'A' 1026 case 0x61: // 'a' 1027 byteValue |= (byte) 0x0A; 1028 break; 1029 case 0x42: // 'B' 1030 case 0x62: // 'b' 1031 byteValue |= (byte) 0x0B; 1032 break; 1033 case 0x43: // 'C' 1034 case 0x63: // 'c' 1035 byteValue |= (byte) 0x0C; 1036 break; 1037 case 0x44: // 'D' 1038 case 0x64: // 'd' 1039 byteValue |= (byte) 0x0D; 1040 break; 1041 case 0x45: // 'E' 1042 case 0x65: // 'e' 1043 byteValue |= (byte) 0x0E; 1044 break; 1045 case 0x46: // 'F' 1046 case 0x66: // 'f' 1047 byteValue |= (byte) 0x0F; 1048 break; 1049 default: 1050 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1051 filterString, equalPos+i+1); 1052 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1053 } 1054 1055 buffer.appendByte(byteValue); 1056 } 1057 else 1058 { 1059 buffer.appendByte(valueBytes[i]); 1060 } 1061 } 1062 1063 subInitial = buffer.toByteString(); 1064 } 1065 else 1066 { 1067 subInitial = ByteString.wrap(valueBytes, 0, firstPos); 1068 } 1069 } 1070 1071 1072 // Next, process through the rest of the asterisks to get the subAny values. 1073 ArrayList<ByteString> subAny = new ArrayList<>(); 1074 for (int asteriskPos : asteriskPositions) 1075 { 1076 int length = asteriskPos - firstPos - 1; 1077 1078 if (hasEscape) 1079 { 1080 ByteStringBuilder buffer = new ByteStringBuilder(length); 1081 for (int i=firstPos+1; i < asteriskPos; i++) 1082 { 1083 if (valueBytes[i] == 0x5C) 1084 { 1085 // The next two bytes must be the hex characters that comprise the 1086 // binary value. 1087 if (i + 2 >= valueBytes.length) 1088 { 1089 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1090 filterString, equalPos+i+1); 1091 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1092 } 1093 1094 byte byteValue = 0; 1095 switch (valueBytes[++i]) 1096 { 1097 case 0x30: // '0' 1098 break; 1099 case 0x31: // '1' 1100 byteValue = (byte) 0x10; 1101 break; 1102 case 0x32: // '2' 1103 byteValue = (byte) 0x20; 1104 break; 1105 case 0x33: // '3' 1106 byteValue = (byte) 0x30; 1107 break; 1108 case 0x34: // '4' 1109 byteValue = (byte) 0x40; 1110 break; 1111 case 0x35: // '5' 1112 byteValue = (byte) 0x50; 1113 break; 1114 case 0x36: // '6' 1115 byteValue = (byte) 0x60; 1116 break; 1117 case 0x37: // '7' 1118 byteValue = (byte) 0x70; 1119 break; 1120 case 0x38: // '8' 1121 byteValue = (byte) 0x80; 1122 break; 1123 case 0x39: // '9' 1124 byteValue = (byte) 0x90; 1125 break; 1126 case 0x41: // 'A' 1127 case 0x61: // 'a' 1128 byteValue = (byte) 0xA0; 1129 break; 1130 case 0x42: // 'B' 1131 case 0x62: // 'b' 1132 byteValue = (byte) 0xB0; 1133 break; 1134 case 0x43: // 'C' 1135 case 0x63: // 'c' 1136 byteValue = (byte) 0xC0; 1137 break; 1138 case 0x44: // 'D' 1139 case 0x64: // 'd' 1140 byteValue = (byte) 0xD0; 1141 break; 1142 case 0x45: // 'E' 1143 case 0x65: // 'e' 1144 byteValue = (byte) 0xE0; 1145 break; 1146 case 0x46: // 'F' 1147 case 0x66: // 'f' 1148 byteValue = (byte) 0xF0; 1149 break; 1150 default: 1151 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1152 filterString, equalPos+i+1); 1153 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1154 } 1155 1156 switch (valueBytes[++i]) 1157 { 1158 case 0x30: // '0' 1159 break; 1160 case 0x31: // '1' 1161 byteValue |= (byte) 0x01; 1162 break; 1163 case 0x32: // '2' 1164 byteValue |= (byte) 0x02; 1165 break; 1166 case 0x33: // '3' 1167 byteValue |= (byte) 0x03; 1168 break; 1169 case 0x34: // '4' 1170 byteValue |= (byte) 0x04; 1171 break; 1172 case 0x35: // '5' 1173 byteValue |= (byte) 0x05; 1174 break; 1175 case 0x36: // '6' 1176 byteValue |= (byte) 0x06; 1177 break; 1178 case 0x37: // '7' 1179 byteValue |= (byte) 0x07; 1180 break; 1181 case 0x38: // '8' 1182 byteValue |= (byte) 0x08; 1183 break; 1184 case 0x39: // '9' 1185 byteValue |= (byte) 0x09; 1186 break; 1187 case 0x41: // 'A' 1188 case 0x61: // 'a' 1189 byteValue |= (byte) 0x0A; 1190 break; 1191 case 0x42: // 'B' 1192 case 0x62: // 'b' 1193 byteValue |= (byte) 0x0B; 1194 break; 1195 case 0x43: // 'C' 1196 case 0x63: // 'c' 1197 byteValue |= (byte) 0x0C; 1198 break; 1199 case 0x44: // 'D' 1200 case 0x64: // 'd' 1201 byteValue |= (byte) 0x0D; 1202 break; 1203 case 0x45: // 'E' 1204 case 0x65: // 'e' 1205 byteValue |= (byte) 0x0E; 1206 break; 1207 case 0x46: // 'F' 1208 case 0x66: // 'f' 1209 byteValue |= (byte) 0x0F; 1210 break; 1211 default: 1212 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1213 filterString, equalPos+i+1); 1214 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1215 } 1216 1217 buffer.appendByte(byteValue); 1218 } 1219 else 1220 { 1221 buffer.appendByte(valueBytes[i]); 1222 } 1223 } 1224 1225 subAny.add(buffer.toByteString()); 1226 buffer.clear(); 1227 } 1228 else 1229 { 1230 subAny.add(ByteString.wrap(valueBytes, firstPos+1, length)); 1231 } 1232 1233 1234 firstPos = asteriskPos; 1235 } 1236 1237 1238 // Finally, see if there is anything after the last asterisk, which would be 1239 // the subFinal value. 1240 ByteString subFinal; 1241 if (firstPos == (valueBytes.length-1)) 1242 { 1243 subFinal = null; 1244 } 1245 else 1246 { 1247 int length = valueBytes.length - firstPos - 1; 1248 1249 if (hasEscape) 1250 { 1251 ByteStringBuilder buffer = new ByteStringBuilder(length); 1252 for (int i=firstPos+1; i < valueBytes.length; i++) 1253 { 1254 if (valueBytes[i] == 0x5C) 1255 { 1256 // The next two bytes must be the hex characters that comprise the 1257 // binary value. 1258 if (i + 2 >= valueBytes.length) 1259 { 1260 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1261 filterString, equalPos+i+1); 1262 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1263 } 1264 1265 byte byteValue = 0; 1266 switch (valueBytes[++i]) 1267 { 1268 case 0x30: // '0' 1269 break; 1270 case 0x31: // '1' 1271 byteValue = (byte) 0x10; 1272 break; 1273 case 0x32: // '2' 1274 byteValue = (byte) 0x20; 1275 break; 1276 case 0x33: // '3' 1277 byteValue = (byte) 0x30; 1278 break; 1279 case 0x34: // '4' 1280 byteValue = (byte) 0x40; 1281 break; 1282 case 0x35: // '5' 1283 byteValue = (byte) 0x50; 1284 break; 1285 case 0x36: // '6' 1286 byteValue = (byte) 0x60; 1287 break; 1288 case 0x37: // '7' 1289 byteValue = (byte) 0x70; 1290 break; 1291 case 0x38: // '8' 1292 byteValue = (byte) 0x80; 1293 break; 1294 case 0x39: // '9' 1295 byteValue = (byte) 0x90; 1296 break; 1297 case 0x41: // 'A' 1298 case 0x61: // 'a' 1299 byteValue = (byte) 0xA0; 1300 break; 1301 case 0x42: // 'B' 1302 case 0x62: // 'b' 1303 byteValue = (byte) 0xB0; 1304 break; 1305 case 0x43: // 'C' 1306 case 0x63: // 'c' 1307 byteValue = (byte) 0xC0; 1308 break; 1309 case 0x44: // 'D' 1310 case 0x64: // 'd' 1311 byteValue = (byte) 0xD0; 1312 break; 1313 case 0x45: // 'E' 1314 case 0x65: // 'e' 1315 byteValue = (byte) 0xE0; 1316 break; 1317 case 0x46: // 'F' 1318 case 0x66: // 'f' 1319 byteValue = (byte) 0xF0; 1320 break; 1321 default: 1322 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1323 filterString, equalPos+i+1); 1324 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1325 } 1326 1327 switch (valueBytes[++i]) 1328 { 1329 case 0x30: // '0' 1330 break; 1331 case 0x31: // '1' 1332 byteValue |= (byte) 0x01; 1333 break; 1334 case 0x32: // '2' 1335 byteValue |= (byte) 0x02; 1336 break; 1337 case 0x33: // '3' 1338 byteValue |= (byte) 0x03; 1339 break; 1340 case 0x34: // '4' 1341 byteValue |= (byte) 0x04; 1342 break; 1343 case 0x35: // '5' 1344 byteValue |= (byte) 0x05; 1345 break; 1346 case 0x36: // '6' 1347 byteValue |= (byte) 0x06; 1348 break; 1349 case 0x37: // '7' 1350 byteValue |= (byte) 0x07; 1351 break; 1352 case 0x38: // '8' 1353 byteValue |= (byte) 0x08; 1354 break; 1355 case 0x39: // '9' 1356 byteValue |= (byte) 0x09; 1357 break; 1358 case 0x41: // 'A' 1359 case 0x61: // 'a' 1360 byteValue |= (byte) 0x0A; 1361 break; 1362 case 0x42: // 'B' 1363 case 0x62: // 'b' 1364 byteValue |= (byte) 0x0B; 1365 break; 1366 case 0x43: // 'C' 1367 case 0x63: // 'c' 1368 byteValue |= (byte) 0x0C; 1369 break; 1370 case 0x44: // 'D' 1371 case 0x64: // 'd' 1372 byteValue |= (byte) 0x0D; 1373 break; 1374 case 0x45: // 'E' 1375 case 0x65: // 'e' 1376 byteValue |= (byte) 0x0E; 1377 break; 1378 case 0x46: // 'F' 1379 case 0x66: // 'f' 1380 byteValue |= (byte) 0x0F; 1381 break; 1382 default: 1383 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1384 filterString, equalPos+i+1); 1385 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1386 } 1387 1388 buffer.appendByte(byteValue); 1389 } 1390 else 1391 { 1392 buffer.appendByte(valueBytes[i]); 1393 } 1394 } 1395 1396 subFinal = buffer.toByteString(); 1397 } 1398 else 1399 { 1400 subFinal = ByteString.wrap(valueBytes, firstPos+1, length); 1401 } 1402 } 1403 1404 1405 return new LDAPFilter(FilterType.SUBSTRING, null, null, attrDesc, null, 1406 subInitial, subAny, subFinal, null, false); 1407 } 1408 1409 1410 1411 /** 1412 * Decodes an extensible match filter component based on the provided 1413 * information. 1414 * 1415 * @param filterString The filter string containing the information to 1416 * decode. 1417 * @param startPos The position in the filter string of the first 1418 * character in the extensible match filter. 1419 * @param equalPos The position of the equal sign in the extensible 1420 * match filter. 1421 * @param endPos The position of the first character after the end of 1422 * the extensible match filter. 1423 * 1424 * @return The decoded LDAP filter. 1425 * 1426 * @throws LDAPException If a problem occurs while attempting to decode the 1427 * extensible match filter. 1428 */ 1429 private static LDAPFilter decodeExtensibleMatchFilter(String filterString, 1430 int startPos, 1431 int equalPos, 1432 int endPos) 1433 throws LDAPException 1434 { 1435 String attributeType = null; 1436 boolean dnAttributes = false; 1437 String matchingRuleID = null; 1438 1439 1440 // Look at the first character. If it is a colon, then it must be followed 1441 // by either the string "dn" or the matching rule ID. If it is not, then 1442 // must be the attribute description. 1443 String lowerLeftStr = 1444 toLowerCase(filterString.substring(startPos, equalPos)); 1445 if (filterString.charAt(startPos) == ':') 1446 { 1447 // See if it starts with ":dn". Otherwise, it much be the matching rule ID. 1448 if (lowerLeftStr.startsWith(":dn:")) 1449 { 1450 dnAttributes = true; 1451 1452 if(startPos+4 < equalPos-1) 1453 { 1454 matchingRuleID = filterString.substring(startPos+4, equalPos-1); 1455 } 1456 } 1457 else 1458 { 1459 matchingRuleID = filterString.substring(startPos+1, equalPos-1); 1460 } 1461 } 1462 else 1463 { 1464 int colonPos = filterString.indexOf(':',startPos); 1465 if (colonPos < 0) 1466 { 1467 LocalizableMessage message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_COLON.get( 1468 filterString, startPos); 1469 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1470 } 1471 1472 1473 attributeType = filterString.substring(startPos, colonPos); 1474 1475 1476 // If there is anything left, then it should be ":dn" and/or ":" followed 1477 // by the matching rule ID. 1478 if (colonPos < equalPos-1) 1479 { 1480 if (lowerLeftStr.startsWith(":dn:", colonPos - startPos)) 1481 { 1482 dnAttributes = true; 1483 1484 if (colonPos+4 < equalPos-1) 1485 { 1486 matchingRuleID = filterString.substring(colonPos+4, equalPos-1); 1487 } 1488 } 1489 else 1490 { 1491 matchingRuleID = filterString.substring(colonPos+1, equalPos-1); 1492 } 1493 } 1494 } 1495 1496 1497 // Parse out the attribute value. 1498 byte[] valueBytes = getBytes(filterString.substring(equalPos+1, endPos)); 1499 boolean hasEscape = false; 1500 for (byte valueByte : valueBytes) 1501 { 1502 if (valueByte == 0x5C) 1503 { 1504 hasEscape = true; 1505 break; 1506 } 1507 } 1508 1509 ByteString value; 1510 if (hasEscape) 1511 { 1512 ByteStringBuilder valueBuffer = new ByteStringBuilder(valueBytes.length); 1513 for (int i=0; i < valueBytes.length; i++) 1514 { 1515 if (valueBytes[i] == 0x5C) // The backslash character 1516 { 1517 // The next two bytes must be the hex characters that comprise the 1518 // binary value. 1519 if (i + 2 >= valueBytes.length) 1520 { 1521 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1522 filterString, equalPos+i+1); 1523 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1524 } 1525 1526 byte byteValue = 0; 1527 switch (valueBytes[++i]) 1528 { 1529 case 0x30: // '0' 1530 break; 1531 case 0x31: // '1' 1532 byteValue = (byte) 0x10; 1533 break; 1534 case 0x32: // '2' 1535 byteValue = (byte) 0x20; 1536 break; 1537 case 0x33: // '3' 1538 byteValue = (byte) 0x30; 1539 break; 1540 case 0x34: // '4' 1541 byteValue = (byte) 0x40; 1542 break; 1543 case 0x35: // '5' 1544 byteValue = (byte) 0x50; 1545 break; 1546 case 0x36: // '6' 1547 byteValue = (byte) 0x60; 1548 break; 1549 case 0x37: // '7' 1550 byteValue = (byte) 0x70; 1551 break; 1552 case 0x38: // '8' 1553 byteValue = (byte) 0x80; 1554 break; 1555 case 0x39: // '9' 1556 byteValue = (byte) 0x90; 1557 break; 1558 case 0x41: // 'A' 1559 case 0x61: // 'a' 1560 byteValue = (byte) 0xA0; 1561 break; 1562 case 0x42: // 'B' 1563 case 0x62: // 'b' 1564 byteValue = (byte) 0xB0; 1565 break; 1566 case 0x43: // 'C' 1567 case 0x63: // 'c' 1568 byteValue = (byte) 0xC0; 1569 break; 1570 case 0x44: // 'D' 1571 case 0x64: // 'd' 1572 byteValue = (byte) 0xD0; 1573 break; 1574 case 0x45: // 'E' 1575 case 0x65: // 'e' 1576 byteValue = (byte) 0xE0; 1577 break; 1578 case 0x46: // 'F' 1579 case 0x66: // 'f' 1580 byteValue = (byte) 0xF0; 1581 break; 1582 default: 1583 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1584 filterString, equalPos+i+1); 1585 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1586 } 1587 1588 switch (valueBytes[++i]) 1589 { 1590 case 0x30: // '0' 1591 break; 1592 case 0x31: // '1' 1593 byteValue |= (byte) 0x01; 1594 break; 1595 case 0x32: // '2' 1596 byteValue |= (byte) 0x02; 1597 break; 1598 case 0x33: // '3' 1599 byteValue |= (byte) 0x03; 1600 break; 1601 case 0x34: // '4' 1602 byteValue |= (byte) 0x04; 1603 break; 1604 case 0x35: // '5' 1605 byteValue |= (byte) 0x05; 1606 break; 1607 case 0x36: // '6' 1608 byteValue |= (byte) 0x06; 1609 break; 1610 case 0x37: // '7' 1611 byteValue |= (byte) 0x07; 1612 break; 1613 case 0x38: // '8' 1614 byteValue |= (byte) 0x08; 1615 break; 1616 case 0x39: // '9' 1617 byteValue |= (byte) 0x09; 1618 break; 1619 case 0x41: // 'A' 1620 case 0x61: // 'a' 1621 byteValue |= (byte) 0x0A; 1622 break; 1623 case 0x42: // 'B' 1624 case 0x62: // 'b' 1625 byteValue |= (byte) 0x0B; 1626 break; 1627 case 0x43: // 'C' 1628 case 0x63: // 'c' 1629 byteValue |= (byte) 0x0C; 1630 break; 1631 case 0x44: // 'D' 1632 case 0x64: // 'd' 1633 byteValue |= (byte) 0x0D; 1634 break; 1635 case 0x45: // 'E' 1636 case 0x65: // 'e' 1637 byteValue |= (byte) 0x0E; 1638 break; 1639 case 0x46: // 'F' 1640 case 0x66: // 'f' 1641 byteValue |= (byte) 0x0F; 1642 break; 1643 default: 1644 LocalizableMessage message = ERR_LDAP_FILTER_INVALID_ESCAPED_BYTE.get( 1645 filterString, equalPos+i+1); 1646 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1647 } 1648 1649 valueBuffer.appendByte(byteValue); 1650 } 1651 else 1652 { 1653 valueBuffer.appendByte(valueBytes[i]); 1654 } 1655 } 1656 1657 value = valueBuffer.toByteString(); 1658 } 1659 else 1660 { 1661 value = ByteString.wrap(valueBytes); 1662 } 1663 1664 1665 // Make sure that the filter has at least one of an attribute description 1666 // and/or a matching rule ID. 1667 if (attributeType == null && matchingRuleID == null) 1668 { 1669 LocalizableMessage message = ERR_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR.get( 1670 filterString, startPos); 1671 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 1672 } 1673 1674 1675 return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null, 1676 attributeType, value, null, null, null, 1677 matchingRuleID, dnAttributes); 1678 } 1679 1680 1681 1682 /** 1683 * Retrieves the filter type for this search filter. 1684 * 1685 * @return The filter type for this search filter. 1686 */ 1687 @Override 1688 public FilterType getFilterType() 1689 { 1690 return filterType; 1691 } 1692 1693 1694 1695 /** 1696 * Retrieves the set of subordinate filter components for AND or OR searches. 1697 * The contents of the returned list may be altered by the caller. 1698 * 1699 * @return The set of subordinate filter components for AND and OR searches, 1700 * or <CODE>null</CODE> if this is not an AND or OR search. 1701 */ 1702 @Override 1703 public ArrayList<RawFilter> getFilterComponents() 1704 { 1705 return filterComponents; 1706 } 1707 1708 1709 1710 /** 1711 * Retrieves the subordinate filter component for NOT searches. 1712 * 1713 * @return The subordinate filter component for NOT searches, or 1714 * <CODE>null</CODE> if this is not a NOT search. 1715 */ 1716 @Override 1717 public RawFilter getNOTComponent() 1718 { 1719 return notComponent; 1720 } 1721 1722 1723 1724 /** 1725 * Retrieves the attribute description for this search filter. This will not be 1726 * applicable for AND, OR, or NOT filters. 1727 * 1728 * @return The attribute description for this search filter, or <CODE>null</CODE> if 1729 * there is none. 1730 */ 1731 @Override 1732 public String getAttributeType() 1733 { 1734 return attributeDescription; 1735 } 1736 1737 1738 1739 /** 1740 * Retrieves the assertion value for this search filter. This will only be 1741 * applicable for equality, greater or equal, less or equal, approximate, or 1742 * extensible matching filters. 1743 * 1744 * @return The assertion value for this search filter, or <CODE>null</CODE> 1745 * if there is none. 1746 */ 1747 @Override 1748 public ByteString getAssertionValue() 1749 { 1750 return assertionValue; 1751 } 1752 1753 1754 1755 /** 1756 * Retrieves the subInitial component for this substring filter. This is only 1757 * applicable for substring search filters, but even substring filters might 1758 * not have a value for this component. 1759 * 1760 * @return The subInitial component for this substring filter, or 1761 * <CODE>null</CODE> if there is none. 1762 */ 1763 @Override 1764 public ByteString getSubInitialElement() 1765 { 1766 return subInitialElement; 1767 } 1768 1769 1770 1771 /** 1772 * Specifies the subInitial element for this substring filter. This will be 1773 * ignored for all other types of filters. 1774 * 1775 * @param subInitialElement The subInitial element for this substring 1776 * filter. 1777 */ 1778 public void setSubInitialElement(ByteString subInitialElement) 1779 { 1780 this.subInitialElement = subInitialElement; 1781 } 1782 1783 1784 1785 /** 1786 * Retrieves the set of subAny elements for this substring filter. This is 1787 * only applicable for substring search filters, and even then may be null or 1788 * empty for some substring filters. 1789 * 1790 * @return The set of subAny elements for this substring filter, or 1791 * <CODE>null</CODE> if there are none. 1792 */ 1793 @Override 1794 public ArrayList<ByteString> getSubAnyElements() 1795 { 1796 return subAnyElements; 1797 } 1798 1799 1800 1801 /** 1802 * Retrieves the subFinal element for this substring filter. This is not 1803 * applicable for any other filter type, and may not be provided even for some 1804 * substring filters. 1805 * 1806 * @return The subFinal element for this substring filter, or 1807 * <CODE>null</CODE> if there is none. 1808 */ 1809 @Override 1810 public ByteString getSubFinalElement() 1811 { 1812 return subFinalElement; 1813 } 1814 1815 1816 1817 /** 1818 * Retrieves the matching rule ID for this extensible match filter. This is 1819 * not applicable for any other type of filter and may not be included in 1820 * some extensible matching filters. 1821 * 1822 * @return The matching rule ID for this extensible match filter, or 1823 * <CODE>null</CODE> if there is none. 1824 */ 1825 @Override 1826 public String getMatchingRuleID() 1827 { 1828 return matchingRuleID; 1829 } 1830 1831 1832 1833 /** 1834 * Retrieves the value of the DN attributes flag for this extensible match 1835 * filter, which indicates whether to perform matching on the components of 1836 * the DN. This does not apply for any other type of filter. 1837 * 1838 * @return The value of the DN attributes flag for this extensibleMatch 1839 * filter. 1840 */ 1841 @Override 1842 public boolean getDNAttributes() 1843 { 1844 return dnAttributes; 1845 } 1846 1847 1848 1849 /** 1850 * Converts this LDAP filter to a search filter that may be used by the 1851 * Directory Server's core processing. 1852 * 1853 * @return The generated search filter. 1854 * 1855 * @throws DirectoryException If a problem occurs while attempting to 1856 * construct the search filter. 1857 */ 1858 @Override 1859 public SearchFilter toSearchFilter() 1860 throws DirectoryException 1861 { 1862 ArrayList<SearchFilter> subComps; 1863 if (filterComponents == null) 1864 { 1865 subComps = null; 1866 } 1867 else 1868 { 1869 int compSize = filterComponents.size(); 1870 if (compSize == 1) 1871 { 1872 // the filter can be simplified to the single component 1873 return filterComponents.get(0).toSearchFilter(); 1874 } 1875 subComps = new ArrayList<>(compSize); 1876 for (RawFilter f : filterComponents) 1877 { 1878 subComps.add(f.toSearchFilter()); 1879 } 1880 } 1881 1882 1883 SearchFilter notComp; 1884 if (notComponent == null) 1885 { 1886 notComp = null; 1887 } 1888 else 1889 { 1890 notComp = notComponent.toSearchFilter(); 1891 } 1892 1893 AttributeDescription attrDesc = null; 1894 if (attributeDescription != null) 1895 { 1896 try 1897 { 1898 attrDesc = AttributeDescription.valueOf(attributeDescription); 1899 } 1900 catch (LocalizedIllegalArgumentException e) 1901 { 1902 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, e.getMessageObject(), e); 1903 } 1904 } 1905 if (assertionValue != null && attrDesc == null) 1906 { 1907 if (matchingRuleID == null) 1908 { 1909 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1910 ERR_LDAP_FILTER_VALUE_WITH_NO_ATTR_OR_MR.get()); 1911 } 1912 1913 try 1914 { 1915 DirectoryServer.getSchema().getMatchingRule(matchingRuleID); 1916 } 1917 catch (UnknownSchemaElementException e) 1918 { 1919 throw new DirectoryException(ResultCode.INAPPROPRIATE_MATCHING, 1920 ERR_LDAP_FILTER_UNKNOWN_MATCHING_RULE.get(matchingRuleID)); 1921 } 1922 } 1923 1924 ArrayList<ByteString> subAnyComps = 1925 subAnyElements != null ? new ArrayList<ByteString>(subAnyElements) : null; 1926 return new SearchFilter(filterType, subComps, notComp, attrDesc, 1927 assertionValue, subInitialElement, subAnyComps, 1928 subFinalElement, matchingRuleID, dnAttributes); 1929 } 1930 1931 /** 1932 * Appends a string representation of this search filter to the provided 1933 * buffer. 1934 * 1935 * @param buffer The buffer to which the information should be appended. 1936 */ 1937 @Override 1938 public void toString(StringBuilder buffer) 1939 { 1940 switch (filterType) 1941 { 1942 case AND: 1943 buffer.append("(&"); 1944 for (RawFilter f : filterComponents) 1945 { 1946 f.toString(buffer); 1947 } 1948 buffer.append(")"); 1949 break; 1950 case OR: 1951 buffer.append("(|"); 1952 for (RawFilter f : filterComponents) 1953 { 1954 f.toString(buffer); 1955 } 1956 buffer.append(")"); 1957 break; 1958 case NOT: 1959 buffer.append("(!"); 1960 notComponent.toString(buffer); 1961 buffer.append(")"); 1962 break; 1963 case EQUALITY: 1964 buffer.append("("); 1965 buffer.append(attributeDescription); 1966 buffer.append("="); 1967 valueToFilterString(buffer, assertionValue); 1968 buffer.append(")"); 1969 break; 1970 case SUBSTRING: 1971 buffer.append("("); 1972 buffer.append(attributeDescription); 1973 buffer.append("="); 1974 1975 if (subInitialElement != null) 1976 { 1977 valueToFilterString(buffer, subInitialElement); 1978 } 1979 1980 if (subAnyElements != null && !subAnyElements.isEmpty()) 1981 { 1982 for (ByteString s : subAnyElements) 1983 { 1984 buffer.append("*"); 1985 valueToFilterString(buffer, s); 1986 } 1987 } 1988 1989 buffer.append("*"); 1990 1991 if (subFinalElement != null) 1992 { 1993 valueToFilterString(buffer, subFinalElement); 1994 } 1995 1996 buffer.append(")"); 1997 break; 1998 case GREATER_OR_EQUAL: 1999 buffer.append("("); 2000 buffer.append(attributeDescription); 2001 buffer.append(">="); 2002 valueToFilterString(buffer, assertionValue); 2003 buffer.append(")"); 2004 break; 2005 case LESS_OR_EQUAL: 2006 buffer.append("("); 2007 buffer.append(attributeDescription); 2008 buffer.append("<="); 2009 valueToFilterString(buffer, assertionValue); 2010 buffer.append(")"); 2011 break; 2012 case PRESENT: 2013 buffer.append("("); 2014 buffer.append(attributeDescription); 2015 buffer.append("=*)"); 2016 break; 2017 case APPROXIMATE_MATCH: 2018 buffer.append("("); 2019 buffer.append(attributeDescription); 2020 buffer.append("~="); 2021 valueToFilterString(buffer, assertionValue); 2022 buffer.append(")"); 2023 break; 2024 case EXTENSIBLE_MATCH: 2025 buffer.append("("); 2026 2027 if (attributeDescription != null) 2028 { 2029 buffer.append(attributeDescription); 2030 } 2031 2032 if (dnAttributes) 2033 { 2034 buffer.append(":dn"); 2035 } 2036 2037 if (matchingRuleID != null) 2038 { 2039 buffer.append(":"); 2040 buffer.append(matchingRuleID); 2041 } 2042 2043 buffer.append(":="); 2044 valueToFilterString(buffer, assertionValue); 2045 buffer.append(")"); 2046 break; 2047 } 2048 } 2049 2050 /** 2051 * Returns the {@code objectClass} presence filter {@code (objectClass=*)}. 2052 * 2053 * @return The {@code objectClass} presence filter {@code (objectClass=*)}. 2054 */ 2055 public static LDAPFilter objectClassPresent() 2056 { 2057 if (objectClassPresent == null) 2058 { 2059 try 2060 { 2061 objectClassPresent = LDAPFilter.decode("(objectclass=*)"); 2062 } 2063 catch (LDAPException canNeverHappen) 2064 { 2065 logger.traceException(canNeverHappen); 2066 } 2067 } 2068 return objectClassPresent; 2069 } 2070}