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