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