001/*
002 * Copyright 2009-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2014 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 */
021package com.unboundid.ldap.protocol;
022
023
024
025import java.io.InterruptedIOException;
026import java.io.IOException;
027import java.io.Serializable;
028import java.net.SocketTimeoutException;
029import java.util.ArrayList;
030import java.util.Arrays;
031import java.util.Collections;
032import java.util.Iterator;
033import java.util.List;
034
035import com.unboundid.asn1.ASN1Buffer;
036import com.unboundid.asn1.ASN1BufferSequence;
037import com.unboundid.asn1.ASN1Element;
038import com.unboundid.asn1.ASN1Integer;
039import com.unboundid.asn1.ASN1Sequence;
040import com.unboundid.asn1.ASN1StreamReader;
041import com.unboundid.asn1.ASN1StreamReaderSequence;
042import com.unboundid.ldap.sdk.Control;
043import com.unboundid.ldap.sdk.InternalSDKHelper;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.ldap.sdk.schema.Schema;
047import com.unboundid.util.InternalUseOnly;
048import com.unboundid.util.NotMutable;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.protocol.ProtocolMessages.*;
053import static com.unboundid.util.Debug.*;
054import 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)
066public 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}