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