001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.protocol;
022    
023    
024    
025    import java.io.InterruptedIOException;
026    import java.io.IOException;
027    import java.io.Serializable;
028    import java.net.SocketTimeoutException;
029    import java.util.ArrayList;
030    import java.util.Arrays;
031    import java.util.Collections;
032    import java.util.Iterator;
033    import java.util.List;
034    
035    import com.unboundid.asn1.ASN1Buffer;
036    import com.unboundid.asn1.ASN1BufferSequence;
037    import com.unboundid.asn1.ASN1Element;
038    import com.unboundid.asn1.ASN1Integer;
039    import com.unboundid.asn1.ASN1Sequence;
040    import com.unboundid.asn1.ASN1StreamReader;
041    import com.unboundid.asn1.ASN1StreamReaderSequence;
042    import com.unboundid.ldap.sdk.Control;
043    import com.unboundid.ldap.sdk.InternalSDKHelper;
044    import com.unboundid.ldap.sdk.LDAPException;
045    import com.unboundid.ldap.sdk.ResultCode;
046    import com.unboundid.ldap.sdk.schema.Schema;
047    import com.unboundid.util.InternalUseOnly;
048    import com.unboundid.util.NotMutable;
049    import com.unboundid.util.ThreadSafety;
050    import com.unboundid.util.ThreadSafetyLevel;
051    
052    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
053    import static com.unboundid.util.Debug.*;
054    import static com.unboundid.util.StaticUtils.*;
055    
056    
057    
058    /**
059     * This class provides a data structure that may be used to represent LDAP
060     * protocol messages.  Each LDAP message contains a message ID, a protocol op,
061     * and an optional set of controls.
062     */
063    @InternalUseOnly()
064    @NotMutable()
065    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
066    public final class LDAPMessage
067           implements Serializable
068    {
069      /**
070       * The BER type to use for the bind request protocol op.
071       */
072      public static final byte PROTOCOL_OP_TYPE_BIND_REQUEST = 0x60;
073    
074    
075    
076      /**
077       * The BER type to use for the bind response protocol op.
078       */
079      public static final byte PROTOCOL_OP_TYPE_BIND_RESPONSE = 0x61;
080    
081    
082    
083      /**
084       * The BER type to use for the unbind request protocol op.
085       */
086      public static final byte PROTOCOL_OP_TYPE_UNBIND_REQUEST = 0x42;
087    
088    
089    
090      /**
091       * The BER type to use for the search request protocol op.
092       */
093      public static final byte PROTOCOL_OP_TYPE_SEARCH_REQUEST = 0x63;
094    
095    
096    
097      /**
098       * The BER type to use for the search result entry protocol op.
099       */
100      public static final byte PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY = 0x64;
101    
102    
103    
104      /**
105       * The BER type to use for the search result reference protocol op.
106       */
107      public static final byte PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73;
108    
109    
110    
111      /**
112       * The BER type to use for the search result done protocol op.
113       */
114      public static final byte PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE = 0x65;
115    
116    
117    
118      /**
119       * The BER type to use for the modify request protocol op.
120       */
121      public static final byte PROTOCOL_OP_TYPE_MODIFY_REQUEST = 0x66;
122    
123    
124    
125      /**
126       * The BER type to use for the modify response protocol op.
127       */
128      public static final byte PROTOCOL_OP_TYPE_MODIFY_RESPONSE = 0x67;
129    
130    
131    
132      /**
133       * The BER type to use for the add request protocol op.
134       */
135      public static final byte PROTOCOL_OP_TYPE_ADD_REQUEST = 0x68;
136    
137    
138    
139      /**
140       * The BER type to use for the add response protocol op.
141       */
142      public static final byte PROTOCOL_OP_TYPE_ADD_RESPONSE = 0x69;
143    
144    
145    
146      /**
147       * The BER type to use for the delete request protocol op.
148       */
149      public static final byte PROTOCOL_OP_TYPE_DELETE_REQUEST = 0x4A;
150    
151    
152    
153      /**
154       * The BER type to use for the delete response protocol op.
155       */
156      public static final byte PROTOCOL_OP_TYPE_DELETE_RESPONSE = 0x6B;
157    
158    
159    
160      /**
161       * The BER type to use for the modify DN request protocol op.
162       */
163      public static final byte PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST = 0x6C;
164    
165    
166    
167      /**
168       * The BER type to use for the modify DN response protocol op.
169       */
170      public static final byte PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE = 0x6D;
171    
172    
173    
174      /**
175       * The BER type to use for the compare request protocol op.
176       */
177      public static final byte PROTOCOL_OP_TYPE_COMPARE_REQUEST = 0x6E;
178    
179    
180    
181      /**
182       * The BER type to use for the compare response protocol op.
183       */
184      public static final byte PROTOCOL_OP_TYPE_COMPARE_RESPONSE = 0x6F;
185    
186    
187    
188      /**
189       * The BER type to use for the abandon request protocol op.
190       */
191      public static final byte PROTOCOL_OP_TYPE_ABANDON_REQUEST = 0x50;
192    
193    
194    
195      /**
196       * The BER type to use for the extended request protocol op.
197       */
198      public static final byte PROTOCOL_OP_TYPE_EXTENDED_REQUEST = 0x77;
199    
200    
201    
202      /**
203       * The BER type to use for the extended response protocol op.
204       */
205      public static final byte PROTOCOL_OP_TYPE_EXTENDED_RESPONSE = 0x78;
206    
207    
208    
209      /**
210       * The BER type to use for the intermediate response protocol op.
211       */
212      public static final byte PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE = 0x79;
213    
214    
215    
216      /**
217       * The BER type to use for the set of controls.
218       */
219      public static final byte MESSAGE_TYPE_CONTROLS = (byte) 0xA0;
220    
221    
222    
223      /**
224       * The serial version UID for this serializable class.
225       */
226      private static final long serialVersionUID = 909272448857832592L;
227    
228    
229    
230      // The message ID for this LDAP message.
231      private final int messageID;
232    
233      // The protocol op for this LDAP message.
234      private final ProtocolOp protocolOp;
235    
236      // The set of controls for this LDAP message.
237      private final List<Control> controls;
238    
239    
240    
241      /**
242       * Creates a new LDAP message with the provided information.
243       *
244       * @param  messageID   The message ID for this LDAP message.
245       * @param  protocolOp  The protocol op for this LDAP message.  It must not be
246       *                     {@code null}.
247       * @param  controls    The set of controls for this LDAP message.  It may be
248       *                     {@code null} or empty if no controls are required.
249       */
250      public LDAPMessage(final int messageID, final ProtocolOp protocolOp,
251                         final Control... controls)
252      {
253        this.messageID  = messageID;
254        this.protocolOp = protocolOp;
255    
256        if (controls == null)
257        {
258          this.controls = Collections.emptyList();
259        }
260        else
261        {
262          this.controls = Collections.unmodifiableList(Arrays.asList(controls));
263        }
264      }
265    
266    
267    
268      /**
269       * Creates a new LDAP message with the provided information.
270       *
271       * @param  messageID   The message ID for this LDAP message.
272       * @param  protocolOp  The protocol op for this LDAP message.  It must not be
273       *                     {@code null}.
274       * @param  controls    The set of controls for this LDAP message.  It may be
275       *                     {@code null} or empty if no controls are required.
276       */
277      public LDAPMessage(final int messageID, final ProtocolOp protocolOp,
278                         final List<Control> controls)
279      {
280        this.messageID  = messageID;
281        this.protocolOp = protocolOp;
282    
283        if (controls == null)
284        {
285          this.controls = Collections.emptyList();
286        }
287        else
288        {
289          this.controls = Collections.unmodifiableList(controls);
290        }
291      }
292    
293    
294    
295      /**
296       * Retrieves the message ID for this LDAP message.
297       *
298       * @return  The message ID for this LDAP message.
299       */
300      public int getMessageID()
301      {
302        return messageID;
303      }
304    
305    
306    
307      /**
308       * Retrieves the protocol op for this LDAP message.
309       *
310       * @return  The protocol op for this LDAP message.
311       */
312      public ProtocolOp getProtocolOp()
313      {
314        return protocolOp;
315      }
316    
317    
318    
319      /**
320       * Retrieves the BER type for the protocol op contained in this LDAP message.
321       *
322       * @return  The BER type for the protocol op contained in this LDAP message.
323       */
324      public byte getProtocolOpType()
325      {
326        return protocolOp.getProtocolOpType();
327      }
328    
329    
330    
331      /**
332       * Retrieves the abandon request protocol op from this LDAP message.  This may
333       * only be used if this LDAP message was obtained using the {@link #readFrom}
334       * method.
335       *
336       * @return  The abandon request protocol op from this LDAP message.
337       *
338       * @throws  ClassCastException  If the protocol op for this LDAP message is
339       *                              not an abandon request protocol op.
340       */
341      public AbandonRequestProtocolOp getAbandonRequestProtocolOp()
342             throws ClassCastException
343      {
344        return (AbandonRequestProtocolOp) protocolOp;
345      }
346    
347    
348    
349      /**
350       * Retrieves the add request protocol op from this LDAP message.  This may
351       * only be used if this LDAP message was obtained using the {@link #readFrom}
352       * method.
353       *
354       * @return  The add request protocol op from this LDAP message.
355       *
356       * @throws  ClassCastException  If the protocol op for this LDAP message is
357       *                              not an add request protocol op.
358       */
359      public AddRequestProtocolOp getAddRequestProtocolOp()
360             throws ClassCastException
361      {
362        return (AddRequestProtocolOp) protocolOp;
363      }
364    
365    
366    
367      /**
368       * Retrieves the add response protocol op from this LDAP message.  This may
369       * only be used if this LDAP message was obtained using the {@link #readFrom}
370       * method.
371       *
372       * @return  The add response protocol op from this LDAP message.
373       *
374       * @throws  ClassCastException  If the protocol op for this LDAP message is
375       *                              not an add response protocol op.
376       */
377      public AddResponseProtocolOp getAddResponseProtocolOp()
378             throws ClassCastException
379      {
380        return (AddResponseProtocolOp) protocolOp;
381      }
382    
383    
384    
385      /**
386       * Retrieves the bind request protocol op from this LDAP message.  This may
387       * only be used if this LDAP message was obtained using the {@link #readFrom}
388       * method.
389       *
390       * @return  The bind request protocol op from this LDAP message.
391       *
392       * @throws  ClassCastException  If the protocol op for this LDAP message is
393       *                              not a bind request protocol op.
394       */
395      public BindRequestProtocolOp getBindRequestProtocolOp()
396             throws ClassCastException
397      {
398        return (BindRequestProtocolOp) protocolOp;
399      }
400    
401    
402    
403      /**
404       * Retrieves the bind response protocol op from this LDAP message.  This may
405       * only be used if this LDAP message was obtained using the {@link #readFrom}
406       * method.
407       *
408       * @return  The bind response protocol op from this LDAP message.
409       *
410       * @throws  ClassCastException  If the protocol op for this LDAP message is
411       *                              not a bind response protocol op.
412       */
413      public BindResponseProtocolOp getBindResponseProtocolOp()
414             throws ClassCastException
415      {
416        return (BindResponseProtocolOp) protocolOp;
417      }
418    
419    
420    
421      /**
422       * Retrieves the compare request protocol op from this LDAP message.  This may
423       * only be used if this LDAP message was obtained using the {@link #readFrom}
424       * method.
425       *
426       * @return  The compare request protocol op from this LDAP message.
427       *
428       * @throws  ClassCastException  If the protocol op for this LDAP message is
429       *                              not a compare request protocol op.
430       */
431      public CompareRequestProtocolOp getCompareRequestProtocolOp()
432             throws ClassCastException
433      {
434        return (CompareRequestProtocolOp) protocolOp;
435      }
436    
437    
438    
439      /**
440       * Retrieves the compare response protocol op from this LDAP message.  This
441       * may only be used if this LDAP message was obtained using the
442       * {@link #readFrom} method.
443       *
444       * @return  The compare response protocol op from this LDAP message.
445       *
446       * @throws  ClassCastException  If the protocol op for this LDAP message is
447       *                              not a compare response protocol op.
448       */
449      public CompareResponseProtocolOp getCompareResponseProtocolOp()
450             throws ClassCastException
451      {
452        return (CompareResponseProtocolOp) protocolOp;
453      }
454    
455    
456    
457      /**
458       * Retrieves the delete request protocol op from this LDAP message.  This may
459       * only be used if this LDAP message was obtained using the {@link #readFrom}
460       * method.
461       *
462       * @return  The delete request protocol op from this LDAP message.
463       *
464       * @throws  ClassCastException  If the protocol op for this LDAP message is
465       *                              not a delete request protocol op.
466       */
467      public DeleteRequestProtocolOp getDeleteRequestProtocolOp()
468             throws ClassCastException
469      {
470        return (DeleteRequestProtocolOp) protocolOp;
471      }
472    
473    
474    
475      /**
476       * Retrieves the delete response protocol op from this LDAP message.  This may
477       * only be used if this LDAP message was obtained using the {@link #readFrom}
478       * method.
479       *
480       * @return  The delete response protocol op from this LDAP message.
481       *
482       * @throws  ClassCastException  If the protocol op for this LDAP message is
483       *                              not a delete response protocol op.
484       */
485      public DeleteResponseProtocolOp getDeleteResponseProtocolOp()
486             throws ClassCastException
487      {
488        return (DeleteResponseProtocolOp) protocolOp;
489      }
490    
491    
492    
493      /**
494       * Retrieves the extended request protocol op from this LDAP message.  This
495       * may only be used if this LDAP message was obtained using the
496       * {@link #readFrom} method.
497       *
498       * @return  The extended request protocol op from this LDAP message.
499       *
500       * @throws  ClassCastException  If the protocol op for this LDAP message is
501       *                              not an extended request protocol op.
502       */
503      public ExtendedRequestProtocolOp getExtendedRequestProtocolOp()
504             throws ClassCastException
505      {
506        return (ExtendedRequestProtocolOp) protocolOp;
507      }
508    
509    
510    
511      /**
512       * Retrieves the extended response protocol op from this LDAP message.  This
513       * may only be used if this LDAP message was obtained using the
514       * {@link #readFrom} method.
515       *
516       * @return  The extended response protocol op from this LDAP message.
517       *
518       * @throws  ClassCastException  If the protocol op for this LDAP message is
519       *                              not an extended response protocol op.
520       */
521      public ExtendedResponseProtocolOp getExtendedResponseProtocolOp()
522             throws ClassCastException
523      {
524        return (ExtendedResponseProtocolOp) protocolOp;
525      }
526    
527    
528    
529      /**
530       * Retrieves the modify request protocol op from this LDAP message.  This may
531       * only be used if this LDAP message was obtained using the {@link #readFrom}
532       * method.
533       *
534       * @return  The modify request protocol op from this LDAP message.
535       *
536       * @throws  ClassCastException  If the protocol op for this LDAP message is
537       *                              not a modify request protocol op.
538       */
539      public ModifyRequestProtocolOp getModifyRequestProtocolOp()
540             throws ClassCastException
541      {
542        return (ModifyRequestProtocolOp) protocolOp;
543      }
544    
545    
546    
547      /**
548       * Retrieves the modify response protocol op from this LDAP message.  This may
549       * only be used if this LDAP message was obtained using the {@link #readFrom}
550       * method.
551       *
552       * @return  The modify response protocol op from this LDAP message.
553       *
554       * @throws  ClassCastException  If the protocol op for this LDAP message is
555       *                              not a modify response protocol op.
556       */
557      public ModifyResponseProtocolOp getModifyResponseProtocolOp()
558             throws ClassCastException
559      {
560        return (ModifyResponseProtocolOp) protocolOp;
561      }
562    
563    
564    
565      /**
566       * Retrieves the modify DN request protocol op from this LDAP message.  This
567       * may only be used if this LDAP message was obtained using the
568       * {@link #readFrom} method.
569       *
570       * @return  The modify DN request protocol op from this LDAP message.
571       *
572       * @throws  ClassCastException  If the protocol op for this LDAP message is
573       *                              not a modify DN request protocol op.
574       */
575      public ModifyDNRequestProtocolOp getModifyDNRequestProtocolOp()
576             throws ClassCastException
577      {
578        return (ModifyDNRequestProtocolOp) protocolOp;
579      }
580    
581    
582    
583      /**
584       * Retrieves the modify DN response protocol op from this LDAP message.  This
585       * may only be used if this LDAP message was obtained using the
586       * {@link #readFrom} method.
587       *
588       * @return  The modify DN response protocol op from this LDAP message.
589       *
590       * @throws  ClassCastException  If the protocol op for this LDAP message is
591       *                              not a modify DN response protocol op.
592       */
593      public ModifyDNResponseProtocolOp getModifyDNResponseProtocolOp()
594             throws ClassCastException
595      {
596        return (ModifyDNResponseProtocolOp) protocolOp;
597      }
598    
599    
600    
601      /**
602       * Retrieves the search request protocol op from this LDAP message.  This
603       * may only be used if this LDAP message was obtained using the
604       * {@link #readFrom} method.
605       *
606       * @return  The search request protocol op from this LDAP message.
607       *
608       * @throws  ClassCastException  If the protocol op for this LDAP message is
609       *                              not a search request protocol op.
610       */
611      public SearchRequestProtocolOp getSearchRequestProtocolOp()
612             throws ClassCastException
613      {
614        return (SearchRequestProtocolOp) protocolOp;
615      }
616    
617    
618    
619      /**
620       * Retrieves the search result entry protocol op from this LDAP message.  This
621       * may only be used if this LDAP message was obtained using the
622       * {@link #readFrom} method.
623       *
624       * @return  The search result entry protocol op from this LDAP message.
625       *
626       * @throws  ClassCastException  If the protocol op for this LDAP message is
627       *                              not a search result entry protocol op.
628       */
629      public SearchResultEntryProtocolOp getSearchResultEntryProtocolOp()
630             throws ClassCastException
631      {
632        return (SearchResultEntryProtocolOp) protocolOp;
633      }
634    
635    
636    
637      /**
638       * Retrieves the search result reference protocol op from this LDAP message.
639       * This may only be used if this LDAP message was obtained using the
640       * {@link #readFrom} method.
641       *
642       * @return  The search result reference protocol op from this LDAP message.
643       *
644       * @throws  ClassCastException  If the protocol op for this LDAP message is
645       *                              not a search result reference protocol op.
646       */
647      public SearchResultReferenceProtocolOp getSearchResultReferenceProtocolOp()
648             throws ClassCastException
649      {
650        return (SearchResultReferenceProtocolOp) protocolOp;
651      }
652    
653    
654    
655      /**
656       * Retrieves the search result done protocol op from this LDAP message.  This
657       * may only be used if this LDAP message was obtained using the
658       * {@link #readFrom} method.
659       *
660       * @return  The search result done protocol op from this LDAP message.
661       *
662       * @throws  ClassCastException  If the protocol op for this LDAP message is
663       *                              not a search result done protocol op.
664       */
665      public SearchResultDoneProtocolOp getSearchResultDoneProtocolOp()
666             throws ClassCastException
667      {
668        return (SearchResultDoneProtocolOp) protocolOp;
669      }
670    
671    
672    
673      /**
674       * Retrieves the unbind request protocol op from this LDAP message.  This may
675       * only be used if this LDAP message was obtained using the {@link #readFrom}
676       * method.
677       *
678       * @return  The unbind request protocol op from this LDAP message.
679       *
680       * @throws  ClassCastException  If the protocol op for this LDAP message is
681       *                              not an unbind request protocol op.
682       */
683      public UnbindRequestProtocolOp getUnbindRequestProtocolOp()
684             throws ClassCastException
685      {
686        return (UnbindRequestProtocolOp) protocolOp;
687      }
688    
689    
690    
691      /**
692       * Retrieves the intermediate response protocol op from this LDAP message.
693       * This may only be used if this LDAP message was obtained using the
694       * {@link #readFrom} method.
695       *
696       * @return  The intermediate response protocol op from this LDAP message.
697       *
698       * @throws  ClassCastException  If the protocol op for this LDAP message is
699       *                              not an intermediate response protocol op.
700       */
701      public IntermediateResponseProtocolOp getIntermediateResponseProtocolOp()
702             throws ClassCastException
703      {
704        return (IntermediateResponseProtocolOp) protocolOp;
705      }
706    
707    
708    
709      /**
710       * Retrieves the set of controls for this LDAP message.
711       *
712       * @return  The set of controls for this LDAP message.
713       */
714      public List<Control> getControls()
715      {
716        return controls;
717      }
718    
719    
720    
721      /**
722       * Encodes this LDAP message to an ASN.1 element.
723       *
724       * @return  The ASN.1 element containing the encoded representation of this
725       *          LDAP message.
726       */
727      public ASN1Element encode()
728      {
729        if (controls.isEmpty())
730        {
731          return new ASN1Sequence(
732               new ASN1Integer(messageID),
733               protocolOp.encodeProtocolOp());
734        }
735        else
736        {
737          final Control[] controlArray = new Control[controls.size()];
738          controls.toArray(controlArray);
739    
740          return new ASN1Sequence(
741               new ASN1Integer(messageID),
742               protocolOp.encodeProtocolOp(),
743               Control.encodeControls(controlArray));
744        }
745      }
746    
747    
748    
749      /**
750       * Decodes the provided ASN.1 element as an LDAP message.
751       *
752       * @param  element  The ASN.1 element to be decoded.
753       *
754       * @return  The LDAP message decoded from the provided ASN.1 element.
755       *
756       * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
757       *                         a valid LDAP message.
758       */
759      public static LDAPMessage decode(final ASN1Element element)
760             throws LDAPException
761      {
762        try
763        {
764          final ASN1Element[] elements =
765               ASN1Sequence.decodeAsSequence(element).elements();
766          if ((elements.length < 2) || (elements.length > 3))
767          {
768            throw new LDAPException(ResultCode.DECODING_ERROR,
769                 ERR_MESSAGE_DECODE_VALUE_SEQUENCE_INVALID_ELEMENT_COUNT.get(
770                      elements.length));
771          }
772    
773          final int messageID = ASN1Integer.decodeAsInteger(elements[0]).intValue();
774    
775          final ProtocolOp protocolOp;
776          switch (elements[1].getType())
777          {
778            case PROTOCOL_OP_TYPE_ABANDON_REQUEST:
779              protocolOp = AbandonRequestProtocolOp.decodeProtocolOp(elements[1]);
780              break;
781            case PROTOCOL_OP_TYPE_ADD_REQUEST:
782              protocolOp = AddRequestProtocolOp.decodeProtocolOp(elements[1]);
783              break;
784            case PROTOCOL_OP_TYPE_ADD_RESPONSE:
785              protocolOp = AddResponseProtocolOp.decodeProtocolOp(elements[1]);
786              break;
787            case PROTOCOL_OP_TYPE_BIND_REQUEST:
788              protocolOp = BindRequestProtocolOp.decodeProtocolOp(elements[1]);
789              break;
790            case PROTOCOL_OP_TYPE_BIND_RESPONSE:
791              protocolOp = BindResponseProtocolOp.decodeProtocolOp(elements[1]);
792              break;
793            case PROTOCOL_OP_TYPE_COMPARE_REQUEST:
794              protocolOp = CompareRequestProtocolOp.decodeProtocolOp(elements[1]);
795              break;
796            case PROTOCOL_OP_TYPE_COMPARE_RESPONSE:
797              protocolOp = CompareResponseProtocolOp.decodeProtocolOp(elements[1]);
798              break;
799            case PROTOCOL_OP_TYPE_DELETE_REQUEST:
800              protocolOp = DeleteRequestProtocolOp.decodeProtocolOp(elements[1]);
801              break;
802            case PROTOCOL_OP_TYPE_DELETE_RESPONSE:
803              protocolOp = DeleteResponseProtocolOp.decodeProtocolOp(elements[1]);
804              break;
805            case PROTOCOL_OP_TYPE_EXTENDED_REQUEST:
806              protocolOp = ExtendedRequestProtocolOp.decodeProtocolOp(elements[1]);
807              break;
808            case PROTOCOL_OP_TYPE_EXTENDED_RESPONSE:
809              protocolOp = ExtendedResponseProtocolOp.decodeProtocolOp(elements[1]);
810              break;
811            case PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE:
812              protocolOp =
813                   IntermediateResponseProtocolOp.decodeProtocolOp(elements[1]);
814              break;
815            case PROTOCOL_OP_TYPE_MODIFY_REQUEST:
816              protocolOp = ModifyRequestProtocolOp.decodeProtocolOp(elements[1]);
817              break;
818            case PROTOCOL_OP_TYPE_MODIFY_RESPONSE:
819              protocolOp = ModifyResponseProtocolOp.decodeProtocolOp(elements[1]);
820              break;
821            case PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST:
822              protocolOp = ModifyDNRequestProtocolOp.decodeProtocolOp(elements[1]);
823              break;
824            case PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE:
825              protocolOp = ModifyDNResponseProtocolOp.decodeProtocolOp(elements[1]);
826              break;
827            case PROTOCOL_OP_TYPE_SEARCH_REQUEST:
828              protocolOp = SearchRequestProtocolOp.decodeProtocolOp(elements[1]);
829              break;
830            case PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE:
831              protocolOp = SearchResultDoneProtocolOp.decodeProtocolOp(elements[1]);
832              break;
833            case PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY:
834              protocolOp =
835                   SearchResultEntryProtocolOp.decodeProtocolOp(elements[1]);
836              break;
837            case PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE:
838              protocolOp =
839                   SearchResultReferenceProtocolOp.decodeProtocolOp(elements[1]);
840              break;
841            case PROTOCOL_OP_TYPE_UNBIND_REQUEST:
842              protocolOp = UnbindRequestProtocolOp.decodeProtocolOp(elements[1]);
843              break;
844            default:
845              throw new LDAPException(ResultCode.DECODING_ERROR,
846                   ERR_MESSAGE_DECODE_INVALID_PROTOCOL_OP_TYPE.get(
847                        toHex(elements[1].getType())));
848          }
849    
850          final Control[] controls;
851          if (elements.length == 3)
852          {
853            controls =
854                 Control.decodeControls(ASN1Sequence.decodeAsSequence(elements[2]));
855          }
856          else
857          {
858            controls = null;
859          }
860    
861          return new LDAPMessage(messageID, protocolOp, controls);
862        }
863        catch (final LDAPException le)
864        {
865          debugException(le);
866          throw le;
867        }
868        catch (final Exception e)
869        {
870          debugException(e);
871          throw new LDAPException(ResultCode.DECODING_ERROR,
872               ERR_MESSAGE_DECODE_ERROR.get(getExceptionMessage(e)),
873               e);
874        }
875      }
876    
877    
878    
879      /**
880       * Writes an encoded representation of this LDAP message to the provided ASN.1
881       * buffer.
882       *
883       * @param  buffer  The ASN.1 buffer to which the encoded representation should
884       *                 be written.
885       */
886      public void writeTo(final ASN1Buffer buffer)
887      {
888        final ASN1BufferSequence messageSequence = buffer.beginSequence();
889        buffer.addInteger(messageID);
890        protocolOp.writeTo(buffer);
891    
892        if (! controls.isEmpty())
893        {
894          final ASN1BufferSequence controlsSequence =
895               buffer.beginSequence(MESSAGE_TYPE_CONTROLS);
896          for (final Control c : controls)
897          {
898            c.writeTo(buffer);
899          }
900          controlsSequence.end();
901        }
902        messageSequence.end();
903      }
904    
905    
906    
907      /**
908       * Reads an LDAP message from the provided ASN.1 stream reader.
909       *
910       * @param  reader               The ASN.1 stream reader from which the LDAP
911       *                              message should be read.
912       * @param  ignoreSocketTimeout  Indicates whether to ignore socket timeout
913       *                              exceptions caught during processing.  This
914       *                              should be {@code true} when the associated
915       *                              connection is operating in asynchronous mode,
916       *                              and {@code false} when operating in
917       *                              synchronous mode.  In either case, exceptions
918       *                              will not be ignored for the first read, since
919       *                              that will be handled by the connection reader.
920       *
921       * @return  The decoded LDAP message, or {@code null} if the end of the input
922       *          stream has been reached..
923       *
924       * @throws  LDAPException  If an error occurs while attempting to read or
925       *                         decode the LDAP message.
926       */
927      public static LDAPMessage readFrom(final ASN1StreamReader reader,
928                                         final boolean ignoreSocketTimeout)
929             throws LDAPException
930      {
931        final ASN1StreamReaderSequence messageSequence;
932        try
933        {
934          reader.setIgnoreSocketTimeout(false, ignoreSocketTimeout);
935          messageSequence = reader.beginSequence();
936          if (messageSequence == null)
937          {
938            return null;
939          }
940        }
941        catch (IOException ioe)
942        {
943          if (! ((ioe instanceof SocketTimeoutException) ||
944                 (ioe instanceof InterruptedIOException)))
945          {
946            debugException(ioe);
947          }
948    
949          throw new LDAPException(ResultCode.SERVER_DOWN,
950               ERR_MESSAGE_IO_ERROR.get(getExceptionMessage(ioe)), ioe);
951        }
952        catch (Exception e)
953        {
954          debugException(e);
955    
956          throw new LDAPException(ResultCode.DECODING_ERROR,
957               ERR_MESSAGE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
958        }
959    
960        try
961        {
962    
963          reader.setIgnoreSocketTimeout(ignoreSocketTimeout, ignoreSocketTimeout);
964          final int messageID = reader.readInteger();
965    
966          final ProtocolOp protocolOp;
967          final byte protocolOpType = (byte) reader.peek();
968          switch (protocolOpType)
969          {
970            case PROTOCOL_OP_TYPE_BIND_REQUEST:
971              protocolOp = new BindRequestProtocolOp(reader);
972              break;
973            case PROTOCOL_OP_TYPE_BIND_RESPONSE:
974              protocolOp = new BindResponseProtocolOp(reader);
975              break;
976            case PROTOCOL_OP_TYPE_UNBIND_REQUEST:
977              protocolOp = new UnbindRequestProtocolOp(reader);
978              break;
979            case PROTOCOL_OP_TYPE_SEARCH_REQUEST:
980              protocolOp = new SearchRequestProtocolOp(reader);
981              break;
982            case PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY:
983              protocolOp = new SearchResultEntryProtocolOp(reader);
984              break;
985            case PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE:
986              protocolOp = new SearchResultReferenceProtocolOp(reader);
987              break;
988            case PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE:
989              protocolOp = new SearchResultDoneProtocolOp(reader);
990              break;
991            case PROTOCOL_OP_TYPE_MODIFY_REQUEST:
992              protocolOp = new ModifyRequestProtocolOp(reader);
993              break;
994            case PROTOCOL_OP_TYPE_MODIFY_RESPONSE:
995              protocolOp = new ModifyResponseProtocolOp(reader);
996              break;
997            case PROTOCOL_OP_TYPE_ADD_REQUEST:
998              protocolOp = new AddRequestProtocolOp(reader);
999              break;
1000            case PROTOCOL_OP_TYPE_ADD_RESPONSE:
1001              protocolOp = new AddResponseProtocolOp(reader);
1002              break;
1003            case PROTOCOL_OP_TYPE_DELETE_REQUEST:
1004              protocolOp = new DeleteRequestProtocolOp(reader);
1005              break;
1006            case PROTOCOL_OP_TYPE_DELETE_RESPONSE:
1007              protocolOp = new DeleteResponseProtocolOp(reader);
1008              break;
1009            case PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST:
1010              protocolOp = new ModifyDNRequestProtocolOp(reader);
1011              break;
1012            case PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE:
1013              protocolOp = new ModifyDNResponseProtocolOp(reader);
1014              break;
1015            case PROTOCOL_OP_TYPE_COMPARE_REQUEST:
1016              protocolOp = new CompareRequestProtocolOp(reader);
1017              break;
1018            case PROTOCOL_OP_TYPE_COMPARE_RESPONSE:
1019              protocolOp = new CompareResponseProtocolOp(reader);
1020              break;
1021            case PROTOCOL_OP_TYPE_ABANDON_REQUEST:
1022              protocolOp = new AbandonRequestProtocolOp(reader);
1023              break;
1024            case PROTOCOL_OP_TYPE_EXTENDED_REQUEST:
1025              protocolOp = new ExtendedRequestProtocolOp(reader);
1026              break;
1027            case PROTOCOL_OP_TYPE_EXTENDED_RESPONSE:
1028              protocolOp = new ExtendedResponseProtocolOp(reader);
1029              break;
1030            case PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE:
1031              protocolOp = new IntermediateResponseProtocolOp(reader);
1032              break;
1033            default:
1034              throw new LDAPException(ResultCode.DECODING_ERROR,
1035                   ERR_MESSAGE_INVALID_PROTOCOL_OP_TYPE.get(toHex(protocolOpType)));
1036          }
1037    
1038          final ArrayList<Control> controls = new ArrayList<Control>(5);
1039          if (messageSequence.hasMoreElements())
1040          {
1041            final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
1042            while (controlSequence.hasMoreElements())
1043            {
1044              controls.add(Control.readFrom(reader));
1045            }
1046          }
1047    
1048          return new LDAPMessage(messageID, protocolOp, controls);
1049        }
1050        catch (LDAPException le)
1051        {
1052          debugException(le);
1053          throw le;
1054        }
1055        catch (IOException ioe)
1056        {
1057          debugException(ioe);
1058    
1059          if ((ioe instanceof SocketTimeoutException) ||
1060              (ioe instanceof InterruptedIOException))
1061          {
1062            // We don't want to provide this exception as the cause because we want
1063            // to ensure that a failure in the middle of the response causes the
1064            // connection to be terminated.
1065            throw new LDAPException(ResultCode.DECODING_ERROR,
1066                 ERR_MESSAGE_CANNOT_DECODE.get(getExceptionMessage(ioe)));
1067          }
1068          else
1069          {
1070            throw new LDAPException(ResultCode.SERVER_DOWN,
1071                 ERR_MESSAGE_IO_ERROR.get(getExceptionMessage(ioe)), ioe);
1072          }
1073        }
1074        catch (Exception e)
1075        {
1076          debugException(e);
1077    
1078          throw new LDAPException(ResultCode.DECODING_ERROR,
1079               ERR_MESSAGE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
1080        }
1081      }
1082    
1083    
1084    
1085      /**
1086       * Reads {@link LDAPResponse} object from the provided ASN.1 stream reader.
1087       *
1088       * @param  reader               The ASN.1 stream reader from which the LDAP
1089       *                              message should be read.
1090       * @param  ignoreSocketTimeout  Indicates whether to ignore socket timeout
1091       *                              exceptions caught during processing.  This
1092       *                              should be {@code true} when the associated
1093       *                              connection is operating in asynchronous mode,
1094       *                              and {@code false} when operating in
1095       *                              synchronous mode.  In either case, exceptions
1096       *                              will not be ignored for the first read, since
1097       *                              that will be handled by the connection reader.
1098       *
1099       * @return  The decoded LDAP message, or {@code null} if the end of the input
1100       *          stream has been reached..
1101       *
1102       * @throws  LDAPException  If an error occurs while attempting to read or
1103       *                         decode the LDAP message.
1104       */
1105      public static LDAPResponse readLDAPResponseFrom(final ASN1StreamReader reader,
1106                                      final boolean ignoreSocketTimeout)
1107             throws LDAPException
1108      {
1109        return readLDAPResponseFrom(reader, ignoreSocketTimeout, null);
1110      }
1111    
1112    
1113    
1114      /**
1115       * Reads {@link LDAPResponse} object from the provided ASN.1 stream reader.
1116       *
1117       * @param  reader               The ASN.1 stream reader from which the LDAP
1118       *                              message should be read.
1119       * @param  ignoreSocketTimeout  Indicates whether to ignore socket timeout
1120       *                              exceptions caught during processing.  This
1121       *                              should be {@code true} when the associated
1122       *                              connection is operating in asynchronous mode,
1123       *                              and {@code false} when operating in
1124       *                              synchronous mode.  In either case, exceptions
1125       *                              will not be ignored for the first read, since
1126       *                              that will be handled by the connection reader.
1127       * @param  schema               The schema to use to select the appropriate
1128       *                              matching rule for attributes included in the
1129       *                              response.
1130       *
1131       * @return  The decoded LDAP message, or {@code null} if the end of the input
1132       *          stream has been reached..
1133       *
1134       * @throws  LDAPException  If an error occurs while attempting to read or
1135       *                         decode the LDAP message.
1136       */
1137      public static LDAPResponse readLDAPResponseFrom(final ASN1StreamReader reader,
1138                                      final boolean ignoreSocketTimeout,
1139                                      final Schema schema)
1140             throws LDAPException
1141      {
1142        final ASN1StreamReaderSequence messageSequence;
1143        try
1144        {
1145          reader.setIgnoreSocketTimeout(false, ignoreSocketTimeout);
1146          messageSequence = reader.beginSequence();
1147          if (messageSequence == null)
1148          {
1149            return null;
1150          }
1151        }
1152        catch (IOException ioe)
1153        {
1154          if (! ((ioe instanceof SocketTimeoutException) ||
1155                 (ioe instanceof InterruptedIOException)))
1156          {
1157            debugException(ioe);
1158          }
1159    
1160          throw new LDAPException(ResultCode.SERVER_DOWN,
1161               ERR_MESSAGE_IO_ERROR.get(getExceptionMessage(ioe)), ioe);
1162        }
1163        catch (Exception e)
1164        {
1165          debugException(e);
1166    
1167          throw new LDAPException(ResultCode.DECODING_ERROR,
1168               ERR_MESSAGE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
1169        }
1170    
1171        try
1172        {
1173          reader.setIgnoreSocketTimeout(ignoreSocketTimeout, ignoreSocketTimeout);
1174          final int messageID = reader.readInteger();
1175    
1176          final byte protocolOpType = (byte) reader.peek();
1177          switch (protocolOpType)
1178          {
1179            case PROTOCOL_OP_TYPE_ADD_RESPONSE:
1180            case PROTOCOL_OP_TYPE_DELETE_RESPONSE:
1181            case PROTOCOL_OP_TYPE_MODIFY_RESPONSE:
1182            case PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE:
1183              return InternalSDKHelper.readLDAPResultFrom(messageID,
1184                          messageSequence, reader);
1185    
1186            case PROTOCOL_OP_TYPE_BIND_RESPONSE:
1187              return InternalSDKHelper.readBindResultFrom(messageID,
1188                          messageSequence, reader);
1189    
1190            case PROTOCOL_OP_TYPE_COMPARE_RESPONSE:
1191              return InternalSDKHelper.readCompareResultFrom(messageID,
1192                          messageSequence, reader);
1193    
1194            case PROTOCOL_OP_TYPE_EXTENDED_RESPONSE:
1195              return InternalSDKHelper.readExtendedResultFrom(messageID,
1196                          messageSequence, reader);
1197    
1198            case PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY:
1199              return InternalSDKHelper.readSearchResultEntryFrom(messageID,
1200                          messageSequence, reader, schema);
1201    
1202            case PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE:
1203              return InternalSDKHelper.readSearchResultReferenceFrom(messageID,
1204                          messageSequence, reader);
1205    
1206            case PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE:
1207              return InternalSDKHelper.readSearchResultFrom(messageID,
1208                          messageSequence, reader);
1209    
1210            case PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE:
1211              return InternalSDKHelper.readIntermediateResponseFrom(messageID,
1212                          messageSequence, reader);
1213    
1214            case PROTOCOL_OP_TYPE_ABANDON_REQUEST:
1215            case PROTOCOL_OP_TYPE_ADD_REQUEST:
1216            case PROTOCOL_OP_TYPE_BIND_REQUEST:
1217            case PROTOCOL_OP_TYPE_COMPARE_REQUEST:
1218            case PROTOCOL_OP_TYPE_DELETE_REQUEST:
1219            case PROTOCOL_OP_TYPE_EXTENDED_REQUEST:
1220            case PROTOCOL_OP_TYPE_MODIFY_REQUEST:
1221            case PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST:
1222            case PROTOCOL_OP_TYPE_SEARCH_REQUEST:
1223            case PROTOCOL_OP_TYPE_UNBIND_REQUEST:
1224              throw new LDAPException(ResultCode.DECODING_ERROR,
1225                   ERR_MESSAGE_PROTOCOL_OP_TYPE_NOT_RESPONSE.get(
1226                        toHex(protocolOpType)));
1227    
1228            default:
1229              throw new LDAPException(ResultCode.DECODING_ERROR,
1230                   ERR_MESSAGE_INVALID_PROTOCOL_OP_TYPE.get(toHex(protocolOpType)));
1231          }
1232        }
1233        catch (LDAPException le)
1234        {
1235          debugException(le);
1236          throw le;
1237        }
1238        catch (IOException ioe)
1239        {
1240          debugException(ioe);
1241    
1242          if ((ioe instanceof SocketTimeoutException) ||
1243              (ioe instanceof InterruptedIOException))
1244          {
1245            // We don't want to provide this exception as the cause because we want
1246            // to ensure that a failure in the middle of the response causes the
1247            // connection to be terminated.
1248            throw new LDAPException(ResultCode.DECODING_ERROR,
1249                 ERR_MESSAGE_CANNOT_DECODE.get(getExceptionMessage(ioe)));
1250          }
1251          else
1252          {
1253            throw new LDAPException(ResultCode.SERVER_DOWN,
1254                 ERR_MESSAGE_IO_ERROR.get(getExceptionMessage(ioe)), ioe);
1255          }
1256        }
1257        catch (Exception e)
1258        {
1259          debugException(e);
1260    
1261          throw new LDAPException(ResultCode.DECODING_ERROR,
1262               ERR_MESSAGE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
1263        }
1264      }
1265    
1266    
1267    
1268      /**
1269       * Retrieves a string representation of this LDAP message.
1270       *
1271       * @return  A string representation of this LDAP message.
1272       */
1273      @Override()
1274      public String toString()
1275      {
1276        final StringBuilder buffer = new StringBuilder();
1277        toString(buffer);
1278        return buffer.toString();
1279      }
1280    
1281    
1282    
1283      /**
1284       * Appends a string representation of this LDAP message to the provided
1285       * buffer.
1286       *
1287       * @param  buffer  The buffer to which the string representation should be
1288       *                 appended.
1289       */
1290      public void toString(final StringBuilder buffer)
1291      {
1292        buffer.append("LDAPMessage(msgID=");
1293        buffer.append(messageID);
1294        buffer.append(", protocolOp=");
1295        protocolOp.toString(buffer);
1296    
1297        if (! controls.isEmpty())
1298        {
1299          buffer.append(", controls={");
1300          final Iterator<Control> iterator = controls.iterator();
1301          while (iterator.hasNext())
1302          {
1303            iterator.next().toString(buffer);
1304            if (iterator.hasNext())
1305            {
1306              buffer.append(',');
1307            }
1308          }
1309          buffer.append('}');
1310        }
1311    
1312        buffer.append(')');
1313      }
1314    }