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