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