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 2009 Sun Microsystems, Inc.
025 *      Portions copyright 2011-2013 ForgeRock AS
026 */
027
028package org.forgerock.opendj.io;
029
030import static com.forgerock.opendj.ldap.CoreMessages.*;
031
032import java.io.IOException;
033
034import org.forgerock.i18n.LocalizedIllegalArgumentException;
035import org.forgerock.i18n.slf4j.LocalizedLogger;
036import org.forgerock.opendj.ldap.Attribute;
037import org.forgerock.opendj.ldap.AttributeDescription;
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.DereferenceAliasesPolicy;
043import org.forgerock.opendj.ldap.Entry;
044import org.forgerock.opendj.ldap.Filter;
045import org.forgerock.opendj.ldap.Modification;
046import org.forgerock.opendj.ldap.ModificationType;
047import org.forgerock.opendj.ldap.RDN;
048import org.forgerock.opendj.ldap.ResultCode;
049import org.forgerock.opendj.ldap.SearchScope;
050import org.forgerock.opendj.ldap.controls.Control;
051import org.forgerock.opendj.ldap.controls.GenericControl;
052import org.forgerock.opendj.ldap.requests.AbandonRequest;
053import org.forgerock.opendj.ldap.requests.AddRequest;
054import org.forgerock.opendj.ldap.requests.CompareRequest;
055import org.forgerock.opendj.ldap.requests.DeleteRequest;
056import org.forgerock.opendj.ldap.requests.GenericBindRequest;
057import org.forgerock.opendj.ldap.requests.GenericExtendedRequest;
058import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
059import org.forgerock.opendj.ldap.requests.ModifyRequest;
060import org.forgerock.opendj.ldap.requests.Request;
061import org.forgerock.opendj.ldap.requests.Requests;
062import org.forgerock.opendj.ldap.requests.SearchRequest;
063import org.forgerock.opendj.ldap.requests.UnbindRequest;
064import org.forgerock.opendj.ldap.responses.BindResult;
065import org.forgerock.opendj.ldap.responses.CompareResult;
066import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
067import org.forgerock.opendj.ldap.responses.GenericIntermediateResponse;
068import org.forgerock.opendj.ldap.responses.Response;
069import org.forgerock.opendj.ldap.responses.Responses;
070import org.forgerock.opendj.ldap.responses.Result;
071import org.forgerock.opendj.ldap.responses.SearchResultEntry;
072import org.forgerock.opendj.ldap.responses.SearchResultReference;
073import org.forgerock.opendj.ldap.schema.Schema;
074
075/**
076 * Reads LDAP messages from an underlying ASN.1 reader.
077 *
078 * @param <R>
079 *            The type of ASN.1 reader used for decoding elements.
080 */
081public final class LDAPReader<R extends ASN1Reader> {
082
083    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
084
085    private final DecodeOptions options;
086    private final R reader;
087
088    LDAPReader(final R asn1Reader, final DecodeOptions options) {
089        this.reader = asn1Reader;
090        this.options = options;
091    }
092
093    /**
094     * Returns the ASN.1 reader from which LDAP messages will be read.
095     *
096     * @return The ASN.1 reader from which LDAP messages will be read.
097     */
098    public R getASN1Reader() {
099        return reader;
100    }
101
102    /**
103     * Returns {@code true} if the next LDAP message can be read without
104     * blocking.
105     *
106     * @return {@code true} if the next LDAP message can be read without
107     *         blocking or {@code false} otherwise.
108     * @throws DecodeException
109     *             If the available data was not a valid LDAP message.
110     * @throws IOException
111     *             If an unexpected IO error occurred.
112     */
113    public boolean hasMessageAvailable() throws DecodeException, IOException {
114        return reader.elementAvailable();
115    }
116
117    /**
118     * Reads the next LDAP message from the underlying ASN.1 reader.
119     *
120     * @param handler
121     *            The message handler which will handle the decoded LDAP
122     *            message.
123     * @throws DecodeException
124     *             If the available data was not a valid LDAP message.
125     * @throws IOException
126     *             If an unexpected IO error occurred.
127     */
128    public void readMessage(final LDAPMessageHandler handler) throws DecodeException, IOException {
129        reader.readStartSequence();
130        try {
131            final int messageID = (int) reader.readInteger();
132            readProtocolOp(messageID, handler);
133        } finally {
134            reader.readEndSequence();
135        }
136    }
137
138    private void readAbandonRequest(final int messageID, final LDAPMessageHandler handler)
139            throws IOException {
140        final int msgToAbandon = (int) reader.readInteger(LDAP.OP_TYPE_ABANDON_REQUEST);
141        final AbandonRequest message = Requests.newAbandonRequest(msgToAbandon);
142        readControls(message);
143        logger.trace("DECODE LDAP ABANDON REQUEST(messageID=%d, request=%s)", messageID, message);
144        handler.abandonRequest(messageID, message);
145    }
146
147    private void readAddRequest(final int messageID, final LDAPMessageHandler handler)
148            throws IOException {
149        final Entry entry = LDAP.readEntry(reader, LDAP.OP_TYPE_ADD_REQUEST, options);
150        final AddRequest message = Requests.newAddRequest(entry);
151        readControls(message);
152        logger.trace("DECODE LDAP ADD REQUEST(messageID=%d, request=%s)", messageID, message);
153        handler.addRequest(messageID, message);
154    }
155
156    private void readAddResult(final int messageID, final LDAPMessageHandler handler)
157            throws IOException {
158        reader.readStartSequence(LDAP.OP_TYPE_ADD_RESPONSE);
159        final Result message;
160        try {
161            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
162            final String matchedDN = reader.readOctetStringAsString();
163            final String diagnosticMessage = reader.readOctetStringAsString();
164            message =
165                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
166                            diagnosticMessage);
167            readResponseReferrals(message);
168        } finally {
169            reader.readEndSequence();
170        }
171        readControls(message);
172        logger.trace("DECODE LDAP ADD RESULT(messageID=%d, result=%s)", messageID, message);
173        handler.addResult(messageID, message);
174    }
175
176    private void readBindRequest(final int messageID, final LDAPMessageHandler handler)
177            throws IOException {
178        reader.readStartSequence(LDAP.OP_TYPE_BIND_REQUEST);
179        try {
180            final int protocolVersion = (int) reader.readInteger();
181            final String authName = reader.readOctetStringAsString();
182            final byte authType = reader.peekType();
183            final byte[] authBytes = reader.readOctetString(authType).toByteArray();
184            final GenericBindRequest request =
185                    Requests.newGenericBindRequest(authName, authType, authBytes);
186            readControls(request);
187            logger.trace("DECODE LDAP BIND REQUEST(messageID=%d, auth=0x%x, request=%s)",
188                messageID, request.getAuthenticationType(), request);
189
190            handler.bindRequest(messageID, protocolVersion, request);
191        } finally {
192            reader.readEndSequence();
193        }
194    }
195
196    private void readBindResult(final int messageID, final LDAPMessageHandler handler)
197            throws IOException {
198        reader.readStartSequence(LDAP.OP_TYPE_BIND_RESPONSE);
199        final BindResult message;
200        try {
201            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
202            final String matchedDN = reader.readOctetStringAsString();
203            final String diagnosticMessage = reader.readOctetStringAsString();
204            message =
205                    Responses.newBindResult(resultCode).setMatchedDN(matchedDN)
206                            .setDiagnosticMessage(diagnosticMessage);
207            readResponseReferrals(message);
208            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_SERVER_SASL_CREDENTIALS)) {
209                message.setServerSASLCredentials(reader
210                        .readOctetString(LDAP.TYPE_SERVER_SASL_CREDENTIALS));
211            }
212        } finally {
213            reader.readEndSequence();
214        }
215        readControls(message);
216        logger.trace("DECODE LDAP BIND RESULT(messageID=%d, result=%s)", messageID, message);
217        handler.bindResult(messageID, message);
218    }
219
220    private void readCompareRequest(final int messageID, final LDAPMessageHandler handler)
221            throws IOException {
222        reader.readStartSequence(LDAP.OP_TYPE_COMPARE_REQUEST);
223        final CompareRequest message;
224        try {
225            final String dnString = reader.readOctetStringAsString();
226            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
227            final DN dn = LDAP.readDN(dnString, schema);
228            reader.readStartSequence();
229            try {
230                final String ads = reader.readOctetStringAsString();
231                final AttributeDescription ad = LDAP.readAttributeDescription(ads, schema);
232                final ByteString assertionValue = reader.readOctetString();
233                message = Requests.newCompareRequest(dn, ad, assertionValue);
234            } finally {
235                reader.readEndSequence();
236            }
237        } finally {
238            reader.readEndSequence();
239        }
240        readControls(message);
241        logger.trace("DECODE LDAP COMPARE REQUEST(messageID=%d, request=%s)", messageID, message);
242        handler.compareRequest(messageID, message);
243    }
244
245    private void readCompareResult(final int messageID, final LDAPMessageHandler handler)
246            throws IOException {
247        reader.readStartSequence(LDAP.OP_TYPE_COMPARE_RESPONSE);
248        final CompareResult message;
249        try {
250            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
251            final String matchedDN = reader.readOctetStringAsString();
252            final String diagnosticMessage = reader.readOctetStringAsString();
253            message =
254                    Responses.newCompareResult(resultCode).setMatchedDN(matchedDN)
255                            .setDiagnosticMessage(diagnosticMessage);
256            readResponseReferrals(message);
257        } finally {
258            reader.readEndSequence();
259        }
260        readControls(message);
261        logger.trace("DECODE LDAP COMPARE RESULT(messageID=%d, result=%s)", messageID, message);
262        handler.compareResult(messageID, message);
263    }
264
265    private Control readControl() throws IOException {
266        reader.readStartSequence();
267        try {
268            final String oid = reader.readOctetStringAsString();
269            final boolean isCritical;
270            if (reader.hasNextElement() && (reader.peekType() == ASN1.UNIVERSAL_BOOLEAN_TYPE)) {
271                isCritical = reader.readBoolean();
272            } else {
273                isCritical = false;
274            }
275            final ByteString value;
276            if (reader.hasNextElement() && (reader.peekType() == ASN1.UNIVERSAL_OCTET_STRING_TYPE)) {
277                value = reader.readOctetString();
278            } else {
279                value = null;
280            }
281            return GenericControl.newControl(oid, isCritical, value);
282        } finally {
283            reader.readEndSequence();
284        }
285    }
286
287    private void readControls(final Request request) throws IOException {
288        if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_CONTROL_SEQUENCE)) {
289            reader.readStartSequence(LDAP.TYPE_CONTROL_SEQUENCE);
290            try {
291                while (reader.hasNextElement()) {
292                    request.addControl(readControl());
293                }
294            } finally {
295                reader.readEndSequence();
296            }
297        }
298    }
299
300    private void readControls(final Response response) throws IOException {
301        if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_CONTROL_SEQUENCE)) {
302            reader.readStartSequence(LDAP.TYPE_CONTROL_SEQUENCE);
303            try {
304                while (reader.hasNextElement()) {
305                    response.addControl(readControl());
306                }
307            } finally {
308                reader.readEndSequence();
309            }
310        }
311    }
312
313    private void readDeleteRequest(final int messageID, final LDAPMessageHandler handler)
314            throws IOException {
315        final String dnString = reader.readOctetStringAsString(LDAP.OP_TYPE_DELETE_REQUEST);
316        final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
317        final DN dn = LDAP.readDN(dnString, schema);
318        final DeleteRequest message = Requests.newDeleteRequest(dn);
319        readControls(message);
320        logger.trace("DECODE LDAP DELETE REQUEST(messageID=%d, request=%s)", messageID, message);
321        handler.deleteRequest(messageID, message);
322    }
323
324    private void readDeleteResult(final int messageID, final LDAPMessageHandler handler)
325            throws IOException {
326        reader.readStartSequence(LDAP.OP_TYPE_DELETE_RESPONSE);
327        final Result message;
328        try {
329            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
330            final String matchedDN = reader.readOctetStringAsString();
331            final String diagnosticMessage = reader.readOctetStringAsString();
332            message =
333                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
334                            diagnosticMessage);
335            readResponseReferrals(message);
336        } finally {
337            reader.readEndSequence();
338        }
339        readControls(message);
340        logger.trace("DECODE LDAP DELETE RESULT(messageID=%d, result=%s)", messageID, message);
341        handler.deleteResult(messageID, message);
342    }
343
344    private void readExtendedRequest(final int messageID, final LDAPMessageHandler handler)
345            throws IOException {
346        reader.readStartSequence(LDAP.OP_TYPE_EXTENDED_REQUEST);
347        final GenericExtendedRequest message;
348        try {
349            final String oid = reader.readOctetStringAsString(LDAP.TYPE_EXTENDED_REQUEST_OID);
350            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_EXTENDED_REQUEST_VALUE)) {
351                final ByteString value = reader.readOctetString(LDAP.TYPE_EXTENDED_REQUEST_VALUE);
352                message = Requests.newGenericExtendedRequest(oid, value);
353            } else {
354                message = Requests.newGenericExtendedRequest(oid, null);
355            }
356        } finally {
357            reader.readEndSequence();
358        }
359        readControls(message);
360        logger.trace("DECODE LDAP EXTENDED REQUEST(messageID=%d, request=%s)", messageID, message);
361        handler.extendedRequest(messageID, message);
362    }
363
364    private void readExtendedResult(final int messageID, final LDAPMessageHandler handler)
365            throws IOException {
366        reader.readStartSequence(LDAP.OP_TYPE_EXTENDED_RESPONSE);
367        final GenericExtendedResult message;
368        try {
369            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
370            final String matchedDN = reader.readOctetStringAsString();
371            final String diagnosticMessage = reader.readOctetStringAsString();
372            message =
373                    Responses.newGenericExtendedResult(resultCode).setMatchedDN(matchedDN)
374                            .setDiagnosticMessage(diagnosticMessage);
375            readResponseReferrals(message);
376            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_EXTENDED_RESPONSE_OID)) {
377                message.setOID(reader.readOctetStringAsString(LDAP.TYPE_EXTENDED_RESPONSE_OID));
378            }
379            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_EXTENDED_RESPONSE_VALUE)) {
380                message.setValue(reader.readOctetString(LDAP.TYPE_EXTENDED_RESPONSE_VALUE));
381            }
382        } finally {
383            reader.readEndSequence();
384        }
385        readControls(message);
386        logger.trace("DECODE LDAP EXTENDED RESULT(messageID=%d, result=%s)", messageID, message);
387        handler.extendedResult(messageID, message);
388    }
389
390    private void readIntermediateResponse(final int messageID, final LDAPMessageHandler handler)
391            throws IOException {
392        reader.readStartSequence(LDAP.OP_TYPE_INTERMEDIATE_RESPONSE);
393        final GenericIntermediateResponse message;
394        try {
395            message = Responses.newGenericIntermediateResponse();
396            if (reader.hasNextElement()
397                    && (reader.peekType() == LDAP.TYPE_INTERMEDIATE_RESPONSE_OID)) {
398                message.setOID(reader.readOctetStringAsString(LDAP.TYPE_INTERMEDIATE_RESPONSE_OID));
399            }
400            if (reader.hasNextElement()
401                    && (reader.peekType() == LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE)) {
402                message.setValue(reader.readOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE));
403            }
404        } finally {
405            reader.readEndSequence();
406        }
407        readControls(message);
408        logger.trace("DECODE LDAP INTERMEDIATE RESPONSE(messageID=%d, response=%s)", messageID, message);
409        handler.intermediateResponse(messageID, message);
410    }
411
412    private void readModifyDNRequest(final int messageID, final LDAPMessageHandler handler)
413            throws IOException {
414        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_DN_REQUEST);
415        final ModifyDNRequest message;
416        try {
417            final String dnString = reader.readOctetStringAsString();
418            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
419            final DN dn = LDAP.readDN(dnString, schema);
420            final String newRDNString = reader.readOctetStringAsString();
421            final RDN newRDN = readRDN(newRDNString, schema);
422            message = Requests.newModifyDNRequest(dn, newRDN);
423            message.setDeleteOldRDN(reader.readBoolean());
424            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR)) {
425                final String newSuperiorString =
426                        reader.readOctetStringAsString(LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR);
427                final DN newSuperior = LDAP.readDN(newSuperiorString, schema);
428                message.setNewSuperior(newSuperior);
429            }
430        } finally {
431            reader.readEndSequence();
432        }
433        readControls(message);
434        logger.trace("DECODE LDAP MODIFY DN REQUEST(messageID=%d, request=%s)", messageID, message);
435        handler.modifyDNRequest(messageID, message);
436    }
437
438    private void readModifyDNResult(final int messageID, final LDAPMessageHandler handler)
439            throws IOException {
440        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_DN_RESPONSE);
441        final Result message;
442        try {
443            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
444            final String matchedDN = reader.readOctetStringAsString();
445            final String diagnosticMessage = reader.readOctetStringAsString();
446            message =
447                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
448                            diagnosticMessage);
449            readResponseReferrals(message);
450        } finally {
451            reader.readEndSequence();
452        }
453        readControls(message);
454        logger.trace("DECODE LDAP MODIFY DN RESULT(messageID=%d, result=%s)", messageID, message);
455        handler.modifyDNResult(messageID, message);
456    }
457
458    private void readModifyRequest(final int messageID, final LDAPMessageHandler handler)
459            throws IOException {
460        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_REQUEST);
461        final ModifyRequest message;
462        try {
463            final String dnString = reader.readOctetStringAsString();
464            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
465            final DN dn = LDAP.readDN(dnString, schema);
466            message = Requests.newModifyRequest(dn);
467            reader.readStartSequence();
468            try {
469                while (reader.hasNextElement()) {
470                    reader.readStartSequence();
471                    try {
472                        final int typeIntValue = reader.readEnumerated();
473                        final ModificationType type = ModificationType.valueOf(typeIntValue);
474                        if (type == null) {
475                            throw DecodeException
476                                    .error(ERR_LDAP_MODIFICATION_DECODE_INVALID_MOD_TYPE
477                                            .get(typeIntValue));
478                        }
479                        reader.readStartSequence();
480                        try {
481                            final String ads = reader.readOctetStringAsString();
482                            final AttributeDescription ad =
483                                    LDAP.readAttributeDescription(ads, schema);
484                            final Attribute attribute =
485                                    options.getAttributeFactory().newAttribute(ad);
486                            reader.readStartSet();
487                            try {
488                                while (reader.hasNextElement()) {
489                                    attribute.add(reader.readOctetString());
490                                }
491                                message.addModification(new Modification(type, attribute));
492                            } finally {
493                                reader.readEndSet();
494                            }
495                        } finally {
496                            reader.readEndSequence();
497                        }
498                    } finally {
499                        reader.readEndSequence();
500                    }
501                }
502            } finally {
503                reader.readEndSequence();
504            }
505        } finally {
506            reader.readEndSequence();
507        }
508        readControls(message);
509        logger.trace("DECODE LDAP MODIFY REQUEST(messageID=%d, request=%s)", messageID, message);
510        handler.modifyRequest(messageID, message);
511    }
512
513    private void readModifyResult(final int messageID, final LDAPMessageHandler handler)
514            throws IOException {
515        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_RESPONSE);
516        final Result message;
517        try {
518            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
519            final String matchedDN = reader.readOctetStringAsString();
520            final String diagnosticMessage = reader.readOctetStringAsString();
521            message =
522                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
523                            diagnosticMessage);
524            readResponseReferrals(message);
525        } finally {
526            reader.readEndSequence();
527        }
528        readControls(message);
529        logger.trace("DECODE LDAP MODIFY RESULT(messageID=%d, result=%s)", messageID, message);
530        handler.modifyResult(messageID, message);
531    }
532
533    private void readProtocolOp(final int messageID, final LDAPMessageHandler handler)
534            throws IOException {
535        final byte type = reader.peekType();
536        switch (type) {
537        case LDAP.OP_TYPE_UNBIND_REQUEST: // 0x42
538            readUnbindRequest(messageID, handler);
539            break;
540        case LDAP.OP_TYPE_DELETE_REQUEST: // 0x4A
541            readDeleteRequest(messageID, handler);
542            break;
543        case LDAP.OP_TYPE_ABANDON_REQUEST: // 0x50
544            readAbandonRequest(messageID, handler);
545            break;
546        case LDAP.OP_TYPE_BIND_REQUEST: // 0x60
547            readBindRequest(messageID, handler);
548            break;
549        case LDAP.OP_TYPE_BIND_RESPONSE: // 0x61
550            readBindResult(messageID, handler);
551            break;
552        case LDAP.OP_TYPE_SEARCH_REQUEST: // 0x63
553            readSearchRequest(messageID, handler);
554            break;
555        case LDAP.OP_TYPE_SEARCH_RESULT_ENTRY: // 0x64
556            readSearchResultEntry(messageID, handler);
557            break;
558        case LDAP.OP_TYPE_SEARCH_RESULT_DONE: // 0x65
559            readSearchResult(messageID, handler);
560            break;
561        case LDAP.OP_TYPE_MODIFY_REQUEST: // 0x66
562            readModifyRequest(messageID, handler);
563            break;
564        case LDAP.OP_TYPE_MODIFY_RESPONSE: // 0x67
565            readModifyResult(messageID, handler);
566            break;
567        case LDAP.OP_TYPE_ADD_REQUEST: // 0x68
568            readAddRequest(messageID, handler);
569            break;
570        case LDAP.OP_TYPE_ADD_RESPONSE: // 0x69
571            readAddResult(messageID, handler);
572            break;
573        case LDAP.OP_TYPE_DELETE_RESPONSE: // 0x6B
574            readDeleteResult(messageID, handler);
575            break;
576        case LDAP.OP_TYPE_MODIFY_DN_REQUEST: // 0x6C
577            readModifyDNRequest(messageID, handler);
578            break;
579        case LDAP.OP_TYPE_MODIFY_DN_RESPONSE: // 0x6D
580            readModifyDNResult(messageID, handler);
581            break;
582        case LDAP.OP_TYPE_COMPARE_REQUEST: // 0x6E
583            readCompareRequest(messageID, handler);
584            break;
585        case LDAP.OP_TYPE_COMPARE_RESPONSE: // 0x6F
586            readCompareResult(messageID, handler);
587            break;
588        case LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE: // 0x73
589            readSearchResultReference(messageID, handler);
590            break;
591        case LDAP.OP_TYPE_EXTENDED_REQUEST: // 0x77
592            readExtendedRequest(messageID, handler);
593            break;
594        case LDAP.OP_TYPE_EXTENDED_RESPONSE: // 0x78
595            readExtendedResult(messageID, handler);
596            break;
597        case LDAP.OP_TYPE_INTERMEDIATE_RESPONSE: // 0x79
598            readIntermediateResponse(messageID, handler);
599            break;
600        default:
601            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
602            break;
603        }
604    }
605
606    private RDN readRDN(final String rdn, final Schema schema) throws DecodeException {
607        try {
608            return RDN.valueOf(rdn, schema);
609        } catch (final LocalizedIllegalArgumentException e) {
610            throw DecodeException.error(e.getMessageObject());
611        }
612    }
613
614    private void readResponseReferrals(final Result message) throws IOException {
615        if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_REFERRAL_SEQUENCE)) {
616            reader.readStartSequence(LDAP.TYPE_REFERRAL_SEQUENCE);
617            try {
618                // Should have at least 1.
619                do {
620                    message.addReferralURI((reader.readOctetStringAsString()));
621                } while (reader.hasNextElement());
622            } finally {
623                reader.readEndSequence();
624            }
625        }
626    }
627
628    private void readSearchRequest(final int messageID, final LDAPMessageHandler handler)
629            throws IOException {
630        reader.readStartSequence(LDAP.OP_TYPE_SEARCH_REQUEST);
631        final SearchRequest message;
632        try {
633            final String baseDNString = reader.readOctetStringAsString();
634            final Schema schema = options.getSchemaResolver().resolveSchema(baseDNString);
635            final DN baseDN = LDAP.readDN(baseDNString, schema);
636            final int scopeIntValue = reader.readEnumerated();
637            final SearchScope scope = SearchScope.valueOf(scopeIntValue);
638            if (scope == null) {
639                throw DecodeException.error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_SCOPE
640                        .get(scopeIntValue));
641            }
642            final int dereferencePolicyIntValue = reader.readEnumerated();
643            final DereferenceAliasesPolicy dereferencePolicy =
644                    DereferenceAliasesPolicy.valueOf(dereferencePolicyIntValue);
645            if (dereferencePolicy == null) {
646                throw DecodeException.error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_DEREF
647                        .get(dereferencePolicyIntValue));
648            }
649            final int sizeLimit = (int) reader.readInteger();
650            final int timeLimit = (int) reader.readInteger();
651            final boolean typesOnly = reader.readBoolean();
652            final Filter filter = LDAP.readFilter(reader);
653            message = Requests.newSearchRequest(baseDN, scope, filter);
654            message.setDereferenceAliasesPolicy(dereferencePolicy);
655            try {
656                message.setTimeLimit(timeLimit);
657                message.setSizeLimit(sizeLimit);
658            } catch (final LocalizedIllegalArgumentException e) {
659                throw DecodeException.error(e.getMessageObject());
660            }
661            message.setTypesOnly(typesOnly);
662            reader.readStartSequence();
663            try {
664                while (reader.hasNextElement()) {
665                    message.addAttribute(reader.readOctetStringAsString());
666                }
667            } finally {
668                reader.readEndSequence();
669            }
670        } finally {
671            reader.readEndSequence();
672        }
673        readControls(message);
674        logger.trace("DECODE LDAP SEARCH REQUEST(messageID=%d, request=%s)", messageID, message);
675        handler.searchRequest(messageID, message);
676    }
677
678    private void readSearchResult(final int messageID, final LDAPMessageHandler handler)
679            throws IOException {
680        reader.readStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_DONE);
681        final Result message;
682        try {
683            final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
684            final String matchedDN = reader.readOctetStringAsString();
685            final String diagnosticMessage = reader.readOctetStringAsString();
686            message =
687                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
688                            diagnosticMessage);
689            readResponseReferrals(message);
690        } finally {
691            reader.readEndSequence();
692        }
693        readControls(message);
694        logger.trace("DECODE LDAP SEARCH RESULT(messageID=%d, result=%s)", messageID, message);
695        handler.searchResult(messageID, message);
696    }
697
698    private void readSearchResultEntry(final int messageID, final LDAPMessageHandler handler)
699            throws IOException {
700        final Entry entry = LDAP.readEntry(reader, LDAP.OP_TYPE_SEARCH_RESULT_ENTRY, options);
701        final SearchResultEntry message = Responses.newSearchResultEntry(entry);
702        readControls(message);
703        logger.trace("DECODE LDAP SEARCH RESULT ENTRY(messageID=%d, entry=%s)", messageID, message);
704        handler.searchResultEntry(messageID, message);
705    }
706
707    private void readSearchResultReference(final int messageID, final LDAPMessageHandler handler)
708            throws IOException {
709        reader.readStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE);
710        final SearchResultReference message;
711        try {
712            message = Responses.newSearchResultReference(reader.readOctetStringAsString());
713            while (reader.hasNextElement()) {
714                message.addURI(reader.readOctetStringAsString());
715            }
716        } finally {
717            reader.readEndSequence();
718        }
719        readControls(message);
720        logger.trace("DECODE LDAP SEARCH RESULT REFERENCE(messageID=%d, reference=%s)", messageID, message);
721        handler.searchResultReference(messageID, message);
722    }
723
724    private void readUnbindRequest(final int messageID, final LDAPMessageHandler handler)
725            throws IOException {
726        reader.readNull(LDAP.OP_TYPE_UNBIND_REQUEST);
727        final UnbindRequest message = Requests.newUnbindRequest();
728        readControls(message);
729        logger.trace("DECODE LDAP UNBIND REQUEST(messageID=%d, request=%s)", messageID, message);
730        handler.unbindRequest(messageID, message);
731    }
732}