001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2013-2016 ForgeRock AS. 015 */ 016package org.forgerock.opendj.io; 017 018import java.io.IOException; 019import java.util.Arrays; 020import java.util.Collections; 021import java.util.LinkedList; 022import java.util.List; 023 024import org.forgerock.i18n.LocalizedIllegalArgumentException; 025import org.forgerock.opendj.ldap.Attribute; 026import org.forgerock.opendj.ldap.AttributeDescription; 027import org.forgerock.opendj.ldap.ByteSequence; 028import org.forgerock.opendj.ldap.ByteString; 029import org.forgerock.opendj.ldap.DN; 030import org.forgerock.opendj.ldap.DecodeException; 031import org.forgerock.opendj.ldap.DecodeOptions; 032import org.forgerock.opendj.ldap.Entry; 033import org.forgerock.opendj.ldap.Filter; 034import org.forgerock.opendj.ldap.FilterVisitor; 035import org.forgerock.opendj.ldap.schema.Schema; 036 037/** 038 * This class contains various static utility methods encoding and decoding LDAP 039 * protocol elements. 040 * 041 * @see LDAPReader 042 * @see LDAPWriter 043 */ 044public final class LDAP { 045 // @Checkstyle:ignore AvoidNestedBlocks 046 047 /** The OID for the Kerberos V GSSAPI mechanism. */ 048 public static final String OID_GSSAPI_KERBEROS_V = "1.2.840.113554.1.2.2"; 049 050 /** The OID for the LDAP notice of disconnection extended operation. */ 051 public static final String OID_NOTICE_OF_DISCONNECTION = "1.3.6.1.4.1.1466.20036"; 052 053 /** The protocol op type for abandon requests. */ 054 public static final byte OP_TYPE_ABANDON_REQUEST = 0x50; 055 056 /** The protocol op type for add requests. */ 057 public static final byte OP_TYPE_ADD_REQUEST = 0x68; 058 059 /** The protocol op type for add responses. */ 060 public static final byte OP_TYPE_ADD_RESPONSE = 0x69; 061 062 /** The protocol op type for bind requests. */ 063 public static final byte OP_TYPE_BIND_REQUEST = 0x60; 064 065 /** The protocol op type for bind responses. */ 066 public static final byte OP_TYPE_BIND_RESPONSE = 0x61; 067 068 /** The protocol op type for compare requests. */ 069 public static final byte OP_TYPE_COMPARE_REQUEST = 0x6E; 070 071 /** The protocol op type for compare responses. */ 072 public static final byte OP_TYPE_COMPARE_RESPONSE = 0x6F; 073 074 /** The protocol op type for delete requests. */ 075 public static final byte OP_TYPE_DELETE_REQUEST = 0x4A; 076 077 /** The protocol op type for delete responses. */ 078 public static final byte OP_TYPE_DELETE_RESPONSE = 0x6B; 079 080 /** The protocol op type for extended requests. */ 081 public static final byte OP_TYPE_EXTENDED_REQUEST = 0x77; 082 083 /** The protocol op type for extended responses. */ 084 public static final byte OP_TYPE_EXTENDED_RESPONSE = 0x78; 085 086 /** The protocol op type for intermediate responses. */ 087 public static final byte OP_TYPE_INTERMEDIATE_RESPONSE = 0x79; 088 089 /** The protocol op type for modify DN requests. */ 090 public static final byte OP_TYPE_MODIFY_DN_REQUEST = 0x6C; 091 092 /** The protocol op type for modify DN responses. */ 093 public static final byte OP_TYPE_MODIFY_DN_RESPONSE = 0x6D; 094 095 /** The protocol op type for modify requests. */ 096 public static final byte OP_TYPE_MODIFY_REQUEST = 0x66; 097 098 /** The protocol op type for modify responses. */ 099 public static final byte OP_TYPE_MODIFY_RESPONSE = 0x67; 100 /** The protocol op type for search requests. */ 101 public static final byte OP_TYPE_SEARCH_REQUEST = 0x63; 102 /** The protocol op type for search result done elements. */ 103 public static final byte OP_TYPE_SEARCH_RESULT_DONE = 0x65; 104 /** The protocol op type for search result entries. */ 105 public static final byte OP_TYPE_SEARCH_RESULT_ENTRY = 0x64; 106 /** The protocol op type for search result references. */ 107 public static final byte OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73; 108 /** The protocol op type for unbind requests. */ 109 public static final byte OP_TYPE_UNBIND_REQUEST = 0x42; 110 /** Mapping between request protocol op and their respective response protocol op. */ 111 public static final byte[] OP_TO_RESULT_TYPE = new byte[0xFF]; 112 static { 113 Arrays.fill(OP_TO_RESULT_TYPE, (byte) 0x00); 114 OP_TO_RESULT_TYPE[OP_TYPE_ADD_REQUEST] = OP_TYPE_ADD_RESPONSE; 115 OP_TO_RESULT_TYPE[OP_TYPE_BIND_REQUEST] = OP_TYPE_BIND_RESPONSE; 116 OP_TO_RESULT_TYPE[OP_TYPE_COMPARE_REQUEST] = OP_TYPE_COMPARE_RESPONSE; 117 OP_TO_RESULT_TYPE[OP_TYPE_DELETE_REQUEST] = OP_TYPE_DELETE_RESPONSE; 118 OP_TO_RESULT_TYPE[OP_TYPE_EXTENDED_REQUEST] = OP_TYPE_EXTENDED_RESPONSE; 119 OP_TO_RESULT_TYPE[OP_TYPE_MODIFY_DN_REQUEST] = OP_TYPE_MODIFY_DN_RESPONSE; 120 OP_TO_RESULT_TYPE[OP_TYPE_MODIFY_REQUEST] = OP_TYPE_MODIFY_RESPONSE; 121 OP_TO_RESULT_TYPE[OP_TYPE_SEARCH_REQUEST] = OP_TYPE_SEARCH_RESULT_DONE; 122 } 123 /** 124 * The BER type to use for the AuthenticationChoice element in a bind 125 * request when SASL authentication is to be used. 126 */ 127 public static final byte TYPE_AUTHENTICATION_SASL = (byte) 0xA3; 128 /** 129 * The BER type to use for the AuthenticationChoice element in a bind 130 * request when simple authentication is to be used. 131 */ 132 public static final byte TYPE_AUTHENTICATION_SIMPLE = (byte) 0x80; 133 /** The BER type to use for encoding the sequence of controls in an LDAP message. */ 134 public static final byte TYPE_CONTROL_SEQUENCE = (byte) 0xA0; 135 /** The BER type to use for the OID of an extended request. */ 136 public static final byte TYPE_EXTENDED_REQUEST_OID = (byte) 0x80; 137 /** The BER type to use for the value of an extended request. */ 138 public static final byte TYPE_EXTENDED_REQUEST_VALUE = (byte) 0x81; 139 /** The BER type to use for the OID of an extended response. */ 140 public static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A; 141 /** The BER type to use for the value of an extended response. */ 142 public static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B; 143 /** The BER type to use for AND filter components. */ 144 public static final byte TYPE_FILTER_AND = (byte) 0xA0; 145 /** The BER type to use for approximate filter components. */ 146 public static final byte TYPE_FILTER_APPROXIMATE = (byte) 0xA8; 147 /** The BER type to use for equality filter components. */ 148 public static final byte TYPE_FILTER_EQUALITY = (byte) 0xA3; 149 /** The BER type to use for extensible matching filter components. */ 150 public static final byte TYPE_FILTER_EXTENSIBLE_MATCH = (byte) 0xA9; 151 /** The BER type to use for greater than or equal to filter components. */ 152 public static final byte TYPE_FILTER_GREATER_OR_EQUAL = (byte) 0xA5; 153 /** The BER type to use for less than or equal to filter components. */ 154 public static final byte TYPE_FILTER_LESS_OR_EQUAL = (byte) 0xA6; 155 /** The BER type to use for NOT filter components. */ 156 public static final byte TYPE_FILTER_NOT = (byte) 0xA2; 157 /** The BER type to use for OR filter components. */ 158 public static final byte TYPE_FILTER_OR = (byte) 0xA1; 159 /** The BER type to use for presence filter components. */ 160 public static final byte TYPE_FILTER_PRESENCE = (byte) 0x87; 161 /** The BER type to use for substring filter components. */ 162 public static final byte TYPE_FILTER_SUBSTRING = (byte) 0xA4; 163 /** The BER type to use for the OID of an intermediate response message. */ 164 public static final byte TYPE_INTERMEDIATE_RESPONSE_OID = (byte) 0x80; 165 /** The BER type to use for the value of an intermediate response message. */ 166 public static final byte TYPE_INTERMEDIATE_RESPONSE_VALUE = (byte) 0x81; 167 /** The BER type to use for the DN attributes flag in a matching rule assertion. */ 168 public static final byte TYPE_MATCHING_RULE_DN_ATTRIBUTES = (byte) 0x84; 169 /** The BER type to use for the matching rule OID in a matching rule assertion. */ 170 public static final byte TYPE_MATCHING_RULE_ID = (byte) 0x81; 171 /** The BER type to use for the attribute type in a matching rule assertion. */ 172 public static final byte TYPE_MATCHING_RULE_TYPE = (byte) 0x82; 173 /** The BER type to use for the assertion value in a matching rule assertion. */ 174 public static final byte TYPE_MATCHING_RULE_VALUE = (byte) 0x83; 175 /** The BER type to use for the newSuperior component of a modify DN request. */ 176 public static final byte TYPE_MODIFY_DN_NEW_SUPERIOR = (byte) 0x80; 177 /** The BER type to use for encoding the sequence of referral URLs in an LDAPResult element. */ 178 public static final byte TYPE_REFERRAL_SEQUENCE = (byte) 0xA3; 179 /** The BER type to use for the server SASL credentials in a bind response. */ 180 public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87; 181 /** The BER type to use for the subAny component(s) of a substring filter. */ 182 public static final byte TYPE_SUBANY = (byte) 0x81; 183 /** The BER type to use for the subFinal components of a substring filter. */ 184 public static final byte TYPE_SUBFINAL = (byte) 0x82; 185 /** The BER type to use for the subInitial component of a substring filter. */ 186 public static final byte TYPE_SUBINITIAL = (byte) 0x80; 187 private static final FilterVisitor<IOException, ASN1Writer> ASN1_ENCODER = 188 new FilterVisitor<IOException, ASN1Writer>() { 189 190 @Override 191 public IOException visitAndFilter(final ASN1Writer writer, 192 final List<Filter> subFilters) { 193 try { 194 writer.writeStartSequence(LDAP.TYPE_FILTER_AND); 195 for (final Filter subFilter : subFilters) { 196 final IOException e = subFilter.accept(this, writer); 197 if (e != null) { 198 return e; 199 } 200 } 201 writer.writeEndSequence(); 202 return null; 203 } catch (final IOException e) { 204 return e; 205 } 206 } 207 208 @Override 209 public IOException visitApproxMatchFilter(final ASN1Writer writer, 210 final String attributeDescription, final ByteString assertionValue) { 211 return writeFilter(writer, LDAP.TYPE_FILTER_APPROXIMATE, attributeDescription, assertionValue); 212 } 213 214 @Override 215 public IOException visitEqualityMatchFilter(final ASN1Writer writer, 216 final String attributeDescription, final ByteString assertionValue) { 217 return writeFilter(writer, LDAP.TYPE_FILTER_EQUALITY, attributeDescription, assertionValue); 218 } 219 220 @Override 221 public IOException visitExtensibleMatchFilter(final ASN1Writer writer, 222 final String matchingRule, final String attributeDescription, 223 final ByteString assertionValue, final boolean dnAttributes) { 224 try { 225 writer.writeStartSequence(LDAP.TYPE_FILTER_EXTENSIBLE_MATCH); 226 227 if (matchingRule != null) { 228 writer.writeOctetString(LDAP.TYPE_MATCHING_RULE_ID, matchingRule); 229 } 230 231 if (attributeDescription != null) { 232 writer.writeOctetString(LDAP.TYPE_MATCHING_RULE_TYPE, attributeDescription); 233 } 234 235 writer.writeOctetString(LDAP.TYPE_MATCHING_RULE_VALUE, assertionValue); 236 237 if (dnAttributes) { 238 writer.writeBoolean(LDAP.TYPE_MATCHING_RULE_DN_ATTRIBUTES, true); 239 } 240 241 writer.writeEndSequence(); 242 return null; 243 } catch (final IOException e) { 244 return e; 245 } 246 } 247 248 @Override 249 public IOException visitGreaterOrEqualFilter(final ASN1Writer writer, 250 final String attributeDescription, final ByteString assertionValue) { 251 return writeFilter(writer, LDAP.TYPE_FILTER_GREATER_OR_EQUAL, attributeDescription, assertionValue); 252 } 253 254 @Override 255 public IOException visitLessOrEqualFilter(final ASN1Writer writer, 256 final String attributeDescription, final ByteString assertionValue) { 257 return writeFilter(writer, LDAP.TYPE_FILTER_LESS_OR_EQUAL, attributeDescription, assertionValue); 258 } 259 260 public IOException writeFilter(final ASN1Writer writer, 261 byte filterType, final String attributeDescription, final ByteString assertionValue) { 262 try { 263 writer.writeStartSequence(filterType); 264 writer.writeOctetString(attributeDescription); 265 writer.writeOctetString(assertionValue); 266 writer.writeEndSequence(); 267 return null; 268 } catch (final IOException e) { 269 return e; 270 } 271 } 272 273 @Override 274 public IOException visitNotFilter(final ASN1Writer writer, final Filter subFilter) { 275 try { 276 writer.writeStartSequence(LDAP.TYPE_FILTER_NOT); 277 final IOException e = subFilter.accept(this, writer); 278 if (e != null) { 279 return e; 280 } 281 writer.writeEndSequence(); 282 return null; 283 } catch (final IOException e) { 284 return e; 285 } 286 } 287 288 @Override 289 public IOException visitOrFilter(final ASN1Writer writer, 290 final List<Filter> subFilters) { 291 try { 292 writer.writeStartSequence(LDAP.TYPE_FILTER_OR); 293 for (final Filter subFilter : subFilters) { 294 final IOException e = subFilter.accept(this, writer); 295 if (e != null) { 296 return e; 297 } 298 } 299 writer.writeEndSequence(); 300 return null; 301 } catch (final IOException e) { 302 return e; 303 } 304 } 305 306 @Override 307 public IOException visitPresentFilter(final ASN1Writer writer, 308 final String attributeDescription) { 309 try { 310 writer.writeOctetString(LDAP.TYPE_FILTER_PRESENCE, attributeDescription); 311 return null; 312 } catch (final IOException e) { 313 return e; 314 } 315 } 316 317 @Override 318 public IOException visitSubstringsFilter(final ASN1Writer writer, 319 final String attributeDescription, final ByteString initialSubstring, 320 final List<ByteString> anySubstrings, final ByteString finalSubstring) { 321 try { 322 writer.writeStartSequence(LDAP.TYPE_FILTER_SUBSTRING); 323 writer.writeOctetString(attributeDescription); 324 325 writer.writeStartSequence(); 326 if (initialSubstring != null) { 327 writer.writeOctetString(LDAP.TYPE_SUBINITIAL, initialSubstring); 328 } 329 330 for (final ByteSequence anySubstring : anySubstrings) { 331 writer.writeOctetString(LDAP.TYPE_SUBANY, anySubstring); 332 } 333 334 if (finalSubstring != null) { 335 writer.writeOctetString(LDAP.TYPE_SUBFINAL, finalSubstring); 336 } 337 writer.writeEndSequence(); 338 339 writer.writeEndSequence(); 340 return null; 341 } catch (final IOException e) { 342 return e; 343 } 344 } 345 346 @Override 347 public IOException visitUnrecognizedFilter(final ASN1Writer writer, 348 final byte filterTag, final ByteString filterBytes) { 349 try { 350 writer.writeOctetString(filterTag, filterBytes); 351 return null; 352 } catch (final IOException e) { 353 return e; 354 } 355 } 356 }; 357 358 /** 359 * Creates a new LDAP reader which will read LDAP messages from an ASN.1 360 * reader using the provided decoding options. 361 * 362 * @param <R> 363 * The type of ASN.1 reader used for decoding elements. 364 * @param asn1Reader 365 * The ASN.1 reader from which LDAP messages will be read. 366 * @param options 367 * LDAP message decoding options. 368 * @return A new LDAP reader which will read LDAP messages from an ASN.1 369 * reader using the provided decoding options. 370 */ 371 public static <R extends ASN1Reader> LDAPReader<R> getReader(final R asn1Reader, 372 final DecodeOptions options) { 373 return new LDAPReader<>(asn1Reader, options); 374 } 375 376 /** 377 * Creates a new LDAP writer which will write LDAP messages to the provided 378 * ASN.1 writer. 379 * 380 * @param <W> 381 * The type of ASN.1 writer used for encoding elements. 382 * @param asn1Writer 383 * The ASN.1 writer to which LDAP messages will be written. 384 * @param ldapVersion 385 * Version of the protocol to use to encode the messages. 386 * @return A new LDAP writer which will write LDAP messages to the provided 387 * ASN.1 writer. 388 */ 389 public static <W extends ASN1Writer> LDAPWriter<W> getWriter(final W asn1Writer, final int ldapVersion) { 390 return new LDAPWriter<>(asn1Writer, ldapVersion); 391 } 392 393 /** 394 * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a 395 * {@code Filter}. 396 * 397 * @param reader 398 * The {@code ASN1Reader} from which the ASN.1 encoded 399 * {@code Filter} should be read. 400 * @return The decoded {@code Filter}. 401 * @throws IOException 402 * If an error occurs while reading from {@code reader}. 403 */ 404 public static Filter readFilter(final ASN1Reader reader) throws IOException { 405 final byte type = reader.peekType(); 406 switch (type) { 407 case LDAP.TYPE_FILTER_AND: 408 return readAndFilter(reader); 409 case LDAP.TYPE_FILTER_OR: 410 return readOrFilter(reader); 411 case LDAP.TYPE_FILTER_NOT: 412 return readNotFilter(reader); 413 case LDAP.TYPE_FILTER_EQUALITY: 414 return readEqualityMatchFilter(reader); 415 case LDAP.TYPE_FILTER_GREATER_OR_EQUAL: 416 return readGreaterOrEqualMatchFilter(reader); 417 case LDAP.TYPE_FILTER_LESS_OR_EQUAL: 418 return readLessOrEqualMatchFilter(reader); 419 case LDAP.TYPE_FILTER_APPROXIMATE: 420 return readApproxMatchFilter(reader); 421 case LDAP.TYPE_FILTER_SUBSTRING: 422 return readSubstringsFilter(reader); 423 case LDAP.TYPE_FILTER_PRESENCE: 424 return Filter.present(reader.readOctetStringAsString(type)); 425 case LDAP.TYPE_FILTER_EXTENSIBLE_MATCH: 426 return readExtensibleMatchFilter(reader); 427 default: 428 return Filter.unrecognized(type, reader.readOctetString(type)); 429 } 430 } 431 432 /** 433 * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a an 434 * {@code Entry}. 435 * 436 * @param reader 437 * The {@code ASN1Reader} from which the ASN.1 encoded 438 * {@code Entry} should be read. 439 * @param options 440 * The decode options to use when decoding the entry. 441 * @return The decoded {@code Entry}. 442 * @throws IOException 443 * If an error occurs while reading from {@code reader}. 444 */ 445 public static Entry readEntry(final ASN1Reader reader, final DecodeOptions options) 446 throws IOException { 447 return readEntry(reader, OP_TYPE_SEARCH_RESULT_ENTRY, options); 448 } 449 450 /** 451 * Writes a {@code Filter} to the provided {@code ASN1Writer}. 452 * 453 * @param writer 454 * The {@code ASN1Writer} to which the ASN.1 encoded 455 * {@code Filter} should be written. 456 * @param filter 457 * The filter. 458 * @throws IOException 459 * If an error occurs while writing to {@code writer}. 460 */ 461 public static void writeFilter(final ASN1Writer writer, final Filter filter) throws IOException { 462 final IOException e = filter.accept(ASN1_ENCODER, writer); 463 if (e != null) { 464 throw e; 465 } 466 } 467 468 /** 469 * Writes an {@code Entry} to the provided {@code ASN1Writer}. 470 * 471 * @param writer 472 * The {@code ASN1Writer} to which the ASN.1 encoded 473 * {@code Entry} should be written. 474 * @param entry 475 * The entry. 476 * @throws IOException 477 * If an error occurs while writing to {@code writer}. 478 */ 479 public static void writeEntry(final ASN1Writer writer, final Entry entry) throws IOException { 480 writeEntry(writer, OP_TYPE_SEARCH_RESULT_ENTRY, entry); 481 } 482 483 static AttributeDescription readAttributeDescription(final String attributeDescription, 484 final Schema schema) throws DecodeException { 485 try { 486 return AttributeDescription.valueOf(attributeDescription, schema); 487 } catch (final LocalizedIllegalArgumentException e) { 488 throw DecodeException.error(e.getMessageObject()); 489 } 490 } 491 492 static DN readDN(final String dn, final Schema schema) throws DecodeException { 493 try { 494 return DN.valueOf(dn, schema); 495 } catch (final LocalizedIllegalArgumentException e) { 496 throw DecodeException.error(e.getMessageObject()); 497 } 498 } 499 500 static Entry readEntry(final ASN1Reader reader, final byte tagType, final DecodeOptions options) 501 throws DecodeException, IOException { 502 reader.readStartSequence(tagType); 503 final Entry entry; 504 try { 505 final String dnString = reader.readOctetStringAsString(); 506 final Schema schema = options.getSchemaResolver().resolveSchema(dnString); 507 final DN dn = readDN(dnString, schema); 508 entry = options.getEntryFactory().newEntry(dn); 509 reader.readStartSequence(); 510 try { 511 while (reader.hasNextElement()) { 512 reader.readStartSequence(); 513 try { 514 final String ads = reader.readOctetStringAsString(); 515 final AttributeDescription ad = readAttributeDescription(ads, schema); 516 final Attribute attribute = options.getAttributeFactory().newAttribute(ad); 517 reader.readStartSet(); 518 try { 519 while (reader.hasNextElement()) { 520 attribute.add(reader.readOctetString()); 521 } 522 entry.addAttribute(attribute); 523 } finally { 524 reader.readEndSet(); 525 } 526 } finally { 527 reader.readEndSequence(); 528 } 529 } 530 } finally { 531 reader.readEndSequence(); 532 } 533 } finally { 534 reader.readEndSequence(); 535 } 536 return entry; 537 } 538 539 static void writeAttribute(final ASN1Writer writer, final Attribute attribute) 540 throws IOException { 541 writer.writeStartSequence(); 542 { 543 writer.writeOctetString(attribute.getAttributeDescriptionAsString()); 544 writer.writeStartSet(); 545 { 546 for (final ByteString value : attribute) { 547 writer.writeOctetString(value); 548 } 549 } 550 writer.writeEndSet(); 551 } 552 writer.writeEndSequence(); 553 } 554 555 static void writeEntry(final ASN1Writer writer, final byte typeTag, final Entry entry) 556 throws IOException { 557 writer.writeStartSequence(typeTag); 558 { 559 writer.writeOctetString(entry.getName().toString()); 560 writer.writeStartSequence(); 561 { 562 for (final Attribute attr : entry.getAllAttributes()) { 563 writeAttribute(writer, attr); 564 } 565 } 566 writer.writeEndSequence(); 567 } 568 writer.writeEndSequence(); 569 } 570 571 private static Filter readAndFilter(final ASN1Reader reader) throws IOException { 572 reader.readStartSequence(LDAP.TYPE_FILTER_AND); 573 try { 574 if (reader.hasNextElement()) { 575 final List<Filter> subFilters = new LinkedList<>(); 576 do { 577 subFilters.add(readFilter(reader)); 578 } while (reader.hasNextElement()); 579 return Filter.and(subFilters); 580 } else { 581 // No sub-filters - this is an RFC 4526 absolute true filter. 582 return Filter.alwaysTrue(); 583 } 584 } finally { 585 reader.readEndSequence(); 586 } 587 } 588 589 private static Filter readApproxMatchFilter(final ASN1Reader reader) throws IOException { 590 reader.readStartSequence(LDAP.TYPE_FILTER_APPROXIMATE); 591 try { 592 final String attributeDescription = reader.readOctetStringAsString(); 593 final ByteString assertionValue = reader.readOctetString(); 594 return Filter.approx(attributeDescription, assertionValue); 595 } finally { 596 reader.readEndSequence(); 597 } 598 } 599 600 private static Filter readEqualityMatchFilter(final ASN1Reader reader) throws IOException { 601 reader.readStartSequence(LDAP.TYPE_FILTER_EQUALITY); 602 try { 603 final String attributeDescription = reader.readOctetStringAsString(); 604 final ByteString assertionValue = reader.readOctetString(); 605 return Filter.equality(attributeDescription, assertionValue); 606 } finally { 607 reader.readEndSequence(); 608 } 609 } 610 611 private static Filter readExtensibleMatchFilter(final ASN1Reader reader) throws IOException { 612 reader.readStartSequence(LDAP.TYPE_FILTER_EXTENSIBLE_MATCH); 613 try { 614 String matchingRule = null; 615 if (reader.peekType() == LDAP.TYPE_MATCHING_RULE_ID) { 616 matchingRule = reader.readOctetStringAsString(LDAP.TYPE_MATCHING_RULE_ID); 617 } 618 String attributeDescription = null; 619 if (reader.peekType() == LDAP.TYPE_MATCHING_RULE_TYPE) { 620 attributeDescription = reader.readOctetStringAsString(LDAP.TYPE_MATCHING_RULE_TYPE); 621 } 622 boolean dnAttributes = false; 623 if (reader.hasNextElement() 624 && (reader.peekType() == LDAP.TYPE_MATCHING_RULE_DN_ATTRIBUTES)) { 625 dnAttributes = reader.readBoolean(); 626 } 627 final ByteString assertionValue = reader.readOctetString(LDAP.TYPE_MATCHING_RULE_VALUE); 628 return Filter.extensible(matchingRule, attributeDescription, assertionValue, 629 dnAttributes); 630 } finally { 631 reader.readEndSequence(); 632 } 633 } 634 635 private static Filter readGreaterOrEqualMatchFilter(final ASN1Reader reader) throws IOException { 636 reader.readStartSequence(LDAP.TYPE_FILTER_GREATER_OR_EQUAL); 637 try { 638 final String attributeDescription = reader.readOctetStringAsString(); 639 final ByteString assertionValue = reader.readOctetString(); 640 return Filter.greaterOrEqual(attributeDescription, assertionValue); 641 } finally { 642 reader.readEndSequence(); 643 } 644 } 645 646 private static Filter readLessOrEqualMatchFilter(final ASN1Reader reader) throws IOException { 647 reader.readStartSequence(LDAP.TYPE_FILTER_LESS_OR_EQUAL); 648 try { 649 final String attributeDescription = reader.readOctetStringAsString(); 650 final ByteString assertionValue = reader.readOctetString(); 651 return Filter.lessOrEqual(attributeDescription, assertionValue); 652 } finally { 653 reader.readEndSequence(); 654 } 655 } 656 657 private static Filter readNotFilter(final ASN1Reader reader) throws IOException { 658 reader.readStartSequence(LDAP.TYPE_FILTER_NOT); 659 try { 660 return Filter.not(readFilter(reader)); 661 } finally { 662 reader.readEndSequence(); 663 } 664 } 665 666 private static Filter readOrFilter(final ASN1Reader reader) throws IOException { 667 reader.readStartSequence(LDAP.TYPE_FILTER_OR); 668 try { 669 if (reader.hasNextElement()) { 670 final List<Filter> subFilters = new LinkedList<>(); 671 do { 672 subFilters.add(readFilter(reader)); 673 } while (reader.hasNextElement()); 674 return Filter.or(subFilters); 675 } else { 676 // No sub-filters - this is an RFC 4526 absolute false filter. 677 return Filter.alwaysFalse(); 678 } 679 } finally { 680 reader.readEndSequence(); 681 } 682 } 683 684 private static Filter readSubstringsFilter(final ASN1Reader reader) throws IOException { 685 reader.readStartSequence(LDAP.TYPE_FILTER_SUBSTRING); 686 try { 687 final String attributeDescription = reader.readOctetStringAsString(); 688 reader.readStartSequence(); 689 try { 690 // FIXME: There should be at least one element in this substring filter sequence. 691 ByteString initialSubstring = null; 692 if (reader.peekType() == LDAP.TYPE_SUBINITIAL) { 693 initialSubstring = reader.readOctetString(LDAP.TYPE_SUBINITIAL); 694 } 695 final List<ByteString> anySubstrings; 696 if (reader.hasNextElement() && reader.peekType() == LDAP.TYPE_SUBANY) { 697 anySubstrings = new LinkedList<>(); 698 do { 699 anySubstrings.add(reader.readOctetString(LDAP.TYPE_SUBANY)); 700 } while (reader.hasNextElement() && reader.peekType() == LDAP.TYPE_SUBANY); 701 } else { 702 anySubstrings = Collections.emptyList(); 703 } 704 ByteString finalSubstring = null; 705 if (reader.hasNextElement() && reader.peekType() == LDAP.TYPE_SUBFINAL) { 706 finalSubstring = reader.readOctetString(LDAP.TYPE_SUBFINAL); 707 } 708 return Filter.substrings(attributeDescription, initialSubstring, anySubstrings, finalSubstring); 709 } finally { 710 reader.readEndSequence(); 711 } 712 } finally { 713 reader.readEndSequence(); 714 } 715 } 716 717 /** Prevent instantiation. */ 718 private LDAP() { 719 // Nothing to do. 720 } 721}