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