001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.protocols.ldap;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    
035    import org.opends.server.api.ProtocolElement;
036    import org.opends.server.protocols.asn1.ASN1Element;
037    import org.opends.server.protocols.asn1.ASN1Integer;
038    import org.opends.server.protocols.asn1.ASN1Sequence;
039    import org.opends.server.types.DebugLogLevel;
040    import org.opends.server.types.LDAPException;
041    
042    import static org.opends.server.loggers.debug.DebugLogger.*;
043    import org.opends.server.loggers.debug.DebugTracer;
044    import static org.opends.messages.ProtocolMessages.*;
045    import static org.opends.server.protocols.ldap.LDAPResultCode.*;
046    import static org.opends.server.util.ServerConstants.*;
047    
048    
049    /**
050     * This class defines the data structures and methods to use when interacting
051     * with an LDAP message, which is the basic envelope used to hold LDAP requests
052     * and responses.
053     */
054    public class LDAPMessage
055           implements ProtocolElement
056    {
057      /**
058       * The tracer object for the debug logger.
059       */
060      private static final DebugTracer TRACER = getTracer();
061    
062      // The set of controls for this LDAP message.
063      private ArrayList<LDAPControl> controls;
064    
065      // The message ID for this LDAP message.
066      private int messageID;
067    
068      // The protocol op for this LDAP message.
069      private ProtocolOp protocolOp;
070    
071    
072    
073      /**
074       * Creates a new LDAP message with the provided message ID and protocol op but
075       * no controls.
076       *
077       * @param  messageID   The message ID for this LDAP message.
078       * @param  protocolOp  The protocol op for this LDAP message.
079       */
080      public LDAPMessage(int messageID, ProtocolOp protocolOp)
081      {
082        this.messageID  = messageID;
083        this.protocolOp = protocolOp;
084    
085        controls = new ArrayList<LDAPControl>(0);
086      }
087    
088    
089    
090      /**
091       * Creates a new LDAP message with the provided message ID, protocol op, and
092       * set of controls.
093       *
094       * @param  messageID   The message ID for this LDAP message.
095       * @param  protocolOp  The protocol op for this LDAP message.
096       * @param  controls    The set of controls for this LDAP message.
097       */
098      public LDAPMessage(int messageID, ProtocolOp protocolOp,
099                         ArrayList<LDAPControl> controls)
100      {
101        this.messageID  = messageID;
102        this.protocolOp = protocolOp;
103    
104        if (controls == null)
105        {
106          this.controls = new ArrayList<LDAPControl>(0);
107        }
108        else
109        {
110          this.controls = controls;
111        }
112      }
113    
114    
115    
116      /**
117       * Retrieves the message ID for this LDAP message.
118       *
119       * @return  The message ID for this LDAP message.
120       */
121      public int getMessageID()
122      {
123        return messageID;
124      }
125    
126    
127    
128      /**
129       * Specifies the message ID for this LDAP message.
130       *
131       * @param  messageID  The message ID for this LDAP message.
132       */
133      public void setMessageID(int messageID)
134      {
135        this.messageID = messageID;
136      }
137    
138    
139    
140      /**
141       * Retrieves the protocol op for this LDAP message.
142       *
143       * @return  The protocol op for this LDAP message.
144       */
145      public ProtocolOp getProtocolOp()
146      {
147        return protocolOp;
148      }
149    
150    
151    
152      /**
153       * Retrieves the protocol op type for this LDAP message.
154       *
155       * @return  The protocol op type for this LDAP message.
156       */
157      public byte getProtocolOpType()
158      {
159        return protocolOp.getType();
160      }
161    
162    
163    
164      /**
165       * Retrieves the protocol op name for this LDAP message.
166       *
167       * @return  The protocol op name for this LDAP message.
168       */
169      public String getProtocolOpName()
170      {
171        return protocolOp.getProtocolOpName();
172      }
173    
174    
175    
176      /**
177       * Retrieves the protocol op for this LDAP message as an abandon request
178       * protocol op.
179       *
180       * @return  The protocol op for this LDAP message as an abandon request
181       *          protocol op.
182       *
183       * @throws  ClassCastException  If the protocol op is not an abandon request
184       *                              protocol op.
185       */
186      public AbandonRequestProtocolOp getAbandonRequestProtocolOp()
187             throws ClassCastException
188      {
189        return (AbandonRequestProtocolOp) protocolOp;
190      }
191    
192    
193    
194      /**
195       * Retrieves the protocol op for this LDAP message as an add request protocol
196       * op.
197       *
198       * @return  The protocol op for this LDAP message as an add request protocol
199       *          op.
200       *
201       * @throws  ClassCastException  If the protocol op is not an add request
202       *                              protocol op.
203       */
204      public AddRequestProtocolOp getAddRequestProtocolOp()
205             throws ClassCastException
206      {
207        return (AddRequestProtocolOp) protocolOp;
208      }
209    
210    
211    
212      /**
213       * Retrieves the protocol op for this LDAP message as an add response protocol
214       * op.
215       *
216       * @return  The protocol op for this LDAP message as an add response protocol
217       *          op.
218       *
219       * @throws  ClassCastException  If the protocol op is not an add response
220       *                              protocol op.
221       */
222      public AddResponseProtocolOp getAddResponseProtocolOp()
223             throws ClassCastException
224      {
225        return (AddResponseProtocolOp) protocolOp;
226      }
227    
228    
229    
230      /**
231       * Retrieves the protocol op for this LDAP message as a bind request
232       * protocol op.
233       *
234       * @return  The protocol op for this LDAP message as a bind request
235       *          protocol op.
236       *
237       * @throws  ClassCastException  If the protocol op is not a bind request
238       *                              protocol op.
239       */
240      public BindRequestProtocolOp getBindRequestProtocolOp()
241             throws ClassCastException
242      {
243        return (BindRequestProtocolOp) protocolOp;
244      }
245    
246    
247    
248      /**
249       * Retrieves the protocol op for this LDAP message as a bind response
250       * protocol op.
251       *
252       * @return  The protocol op for this LDAP message as a bind response
253       *          protocol op.
254       *
255       * @throws  ClassCastException  If the protocol op is not a bind response
256       *                              protocol op.
257       */
258      public BindResponseProtocolOp getBindResponseProtocolOp()
259             throws ClassCastException
260      {
261        return (BindResponseProtocolOp) protocolOp;
262      }
263    
264    
265    
266      /**
267       * Retrieves the protocol op for this LDAP message as a compare request
268       * protocol op.
269       *
270       * @return  The protocol op for this LDAP message as a compare request
271       *          protocol op.
272       *
273       * @throws  ClassCastException  If the protocol op is not a compare request
274       *                              protocol op.
275       */
276      public CompareRequestProtocolOp getCompareRequestProtocolOp()
277             throws ClassCastException
278      {
279        return (CompareRequestProtocolOp) protocolOp;
280      }
281    
282    
283    
284      /**
285       * Retrieves the protocol op for this LDAP message as a compare response
286       * protocol op.
287       *
288       * @return  The protocol op for this LDAP message as a compare response
289       *          protocol op.
290       *
291       * @throws  ClassCastException  If the protocol op is not a compare response
292       *                              protocol op.
293       */
294      public CompareResponseProtocolOp getCompareResponseProtocolOp()
295             throws ClassCastException
296      {
297        return (CompareResponseProtocolOp) protocolOp;
298      }
299    
300    
301    
302      /**
303       * Retrieves the protocol op for this LDAP message as a delete request
304       * protocol op.
305       *
306       * @return  The protocol op for this LDAP message as a delete request
307       *          protocol op.
308       *
309       * @throws  ClassCastException  If the protocol op is not a delete request
310       *                              protocol op.
311       */
312      public DeleteRequestProtocolOp getDeleteRequestProtocolOp()
313             throws ClassCastException
314      {
315        return (DeleteRequestProtocolOp) protocolOp;
316      }
317    
318    
319    
320      /**
321       * Retrieves the protocol op for this LDAP message as a delete response
322       * protocol op.
323       *
324       * @return  The protocol op for this LDAP message as a delete response
325       *          protocol op.
326       *
327       * @throws  ClassCastException  If the protocol op is not a delete response
328       *                              protocol op.
329       */
330      public DeleteResponseProtocolOp getDeleteResponseProtocolOp()
331             throws ClassCastException
332      {
333        return (DeleteResponseProtocolOp) protocolOp;
334      }
335    
336    
337    
338      /**
339       * Retrieves the protocol op for this LDAP message as an extended request
340       * protocol op.
341       *
342       * @return  The protocol op for this LDAP message as an extended request
343       *          protocol op.
344       *
345       * @throws  ClassCastException  If the protocol op is not an extended request
346       *                              protocol op.
347       */
348      public ExtendedRequestProtocolOp getExtendedRequestProtocolOp()
349             throws ClassCastException
350      {
351        return (ExtendedRequestProtocolOp) protocolOp;
352      }
353    
354    
355    
356      /**
357       * Retrieves the protocol op for this LDAP message as an extended response
358       * protocol op.
359       *
360       * @return  The protocol op for this LDAP message as an extended response
361       *          protocol op.
362       *
363       * @throws  ClassCastException  If the protocol op is not an extended response
364       *                              protocol op.
365       */
366      public ExtendedResponseProtocolOp getExtendedResponseProtocolOp()
367             throws ClassCastException
368      {
369        return (ExtendedResponseProtocolOp) protocolOp;
370      }
371    
372    
373    
374      /**
375       * Retrieves the protocol op for this LDAP message as a modify request
376       * protocol op.
377       *
378       * @return  The protocol op for this LDAP message as a modify request
379       *          protocol op.
380       *
381       * @throws  ClassCastException  If the protocol op is not a modify request
382       *                              protocol op.
383       */
384      public ModifyRequestProtocolOp getModifyRequestProtocolOp()
385             throws ClassCastException
386      {
387        return (ModifyRequestProtocolOp) protocolOp;
388      }
389    
390    
391    
392      /**
393       * Retrieves the protocol op for this LDAP message as a modify response
394       * protocol op.
395       *
396       * @return  The protocol op for this LDAP message as a modify response
397       *          protocol op.
398       *
399       * @throws  ClassCastException  If the protocol op is not a modify response
400       *                              protocol op.
401       */
402      public ModifyResponseProtocolOp getModifyResponseProtocolOp()
403             throws ClassCastException
404      {
405        return (ModifyResponseProtocolOp) protocolOp;
406      }
407    
408    
409    
410      /**
411       * Retrieves the protocol op for this LDAP message as a modify DN request
412       * protocol op.
413       *
414       * @return  The protocol op for this LDAP message as a modify DN request
415       *          protocol op.
416       *
417       * @throws  ClassCastException  If the protocol op is not a modify DN request
418       *                              protocol op.
419       */
420      public ModifyDNRequestProtocolOp getModifyDNRequestProtocolOp()
421             throws ClassCastException
422      {
423        return (ModifyDNRequestProtocolOp) protocolOp;
424      }
425    
426    
427    
428      /**
429       * Retrieves the protocol op for this LDAP message as a modify DN response
430       * protocol op.
431       *
432       * @return  The protocol op for this LDAP message as a modify DN response
433       *          protocol op.
434       *
435       * @throws  ClassCastException  If the protocol op is not a modify DN response
436       *                              protocol op.
437       */
438      public ModifyDNResponseProtocolOp getModifyDNResponseProtocolOp()
439             throws ClassCastException
440      {
441        return (ModifyDNResponseProtocolOp) protocolOp;
442      }
443    
444    
445    
446      /**
447       * Retrieves the protocol op for this LDAP message as a search request
448       * protocol op.
449       *
450       * @return  The protocol op for this LDAP message as a search request
451       *          protocol op.
452       *
453       * @throws  ClassCastException  If the protocol op is not a search request
454       *                              protocol op.
455       */
456      public SearchRequestProtocolOp getSearchRequestProtocolOp()
457             throws ClassCastException
458      {
459        return (SearchRequestProtocolOp) protocolOp;
460      }
461    
462    
463    
464      /**
465       * Retrieves the protocol op for this LDAP message as a search result done
466       * protocol op.
467       *
468       * @return  The protocol op for this LDAP message as a search result done
469       *          protocol op.
470       *
471       * @throws  ClassCastException  If the protocol op is not a search result done
472       *                              protocol op.
473       */
474      public SearchResultDoneProtocolOp getSearchResultDoneProtocolOp()
475             throws ClassCastException
476      {
477        return (SearchResultDoneProtocolOp) protocolOp;
478      }
479    
480    
481    
482      /**
483       * Retrieves the protocol op for this LDAP message as a search result entry
484       * protocol op.
485       *
486       * @return  The protocol op for this LDAP message as a search result entry
487       *          protocol op.
488       *
489       * @throws  ClassCastException  If the protocol op is not a search result
490       *                              entry protocol op.
491       */
492      public SearchResultEntryProtocolOp getSearchResultEntryProtocolOp()
493             throws ClassCastException
494      {
495        return (SearchResultEntryProtocolOp) protocolOp;
496      }
497    
498    
499    
500      /**
501       * Retrieves the protocol op for this LDAP message as a search result
502       * reference protocol op.
503       *
504       * @return  The protocol op for this LDAP message as a search result reference
505       *          protocol op.
506       *
507       * @throws  ClassCastException  If the protocol op is not a search result
508       *                              reference protocol op.
509       */
510      public SearchResultReferenceProtocolOp getSearchResultReferenceProtocolOp()
511             throws ClassCastException
512      {
513        return (SearchResultReferenceProtocolOp) protocolOp;
514      }
515    
516    
517    
518      /**
519       * Retrieves the protocol op for this LDAP message as an unbind request
520       * protocol op.
521       *
522       * @return  The protocol op for this LDAP message as an unbind request
523       *          protocol op.
524       *
525       * @throws  ClassCastException  If the protocol op is not an unbind request
526       *                              protocol op.
527       */
528      public UnbindRequestProtocolOp getUnbindRequestProtocolOp()
529             throws ClassCastException
530      {
531        return (UnbindRequestProtocolOp) protocolOp;
532      }
533    
534    
535    
536      /**
537       * Specifies the protocol op for this LDAP message.
538       *
539       * @param  protocolOp  The protocol op for this LDAP message.
540       */
541      public void setProtocolOp(ProtocolOp protocolOp)
542      {
543        this.protocolOp = protocolOp;
544      }
545    
546    
547    
548      /**
549       * Retrieves the set of controls for this LDAP message.  It may be modified by
550       * the caller.
551       *
552       * @return  The set of controls for this LDAP message.
553       */
554      public ArrayList<LDAPControl> getControls()
555      {
556        return controls;
557      }
558    
559    
560    
561      /**
562       * Encodes this LDAP message to an ASN.1 element.
563       *
564       * @return  The ASN.1 element containing the encoded LDAP message.
565       */
566      public ASN1Element encode()
567      {
568        ArrayList<ASN1Element> messageElements = new ArrayList<ASN1Element>(3);
569        messageElements.add(new ASN1Integer(messageID));
570        messageElements.add(protocolOp.encode());
571    
572        if (! controls.isEmpty())
573        {
574          messageElements.add(LDAPControl.encodeControls(controls));
575        }
576    
577        return new ASN1Sequence(messageElements);
578      }
579    
580    
581    
582      /**
583       * Decodes the provided ASN.1 sequence as an LDAP message.
584       *
585       * @param  messageSequence  The ASN.1 sequence to decode as an LDAP message.
586       *
587       * @return  The decoded LDAP message.
588       *
589       * @throws  LDAPException  If a problem occurs while attempting to decode the
590       *                         LDAP message.
591       */
592      public static LDAPMessage decode(ASN1Sequence messageSequence)
593             throws LDAPException
594      {
595        if (messageSequence == null)
596        {
597          Message message = ERR_LDAP_MESSAGE_DECODE_NULL.get();
598          throw new LDAPException(PROTOCOL_ERROR, message);
599        }
600    
601        ArrayList<ASN1Element> elements = messageSequence.elements();
602        int numElements = elements.size();
603        if ((numElements < 2) || (numElements > 3))
604        {
605          Message message =
606              ERR_LDAP_MESSAGE_DECODE_INVALID_ELEMENT_COUNT.get(numElements);
607          throw new LDAPException(PROTOCOL_ERROR, message);
608        }
609    
610    
611        int messageID;
612        try
613        {
614          messageID = elements.get(0).decodeAsInteger().intValue();
615        }
616        catch (Exception e)
617        {
618          if (debugEnabled())
619          {
620            TRACER.debugCaught(DebugLogLevel.ERROR, e);
621          }
622    
623          Message message =
624              ERR_LDAP_MESSAGE_DECODE_MESSAGE_ID.get(String.valueOf(e));
625          throw new LDAPException(PROTOCOL_ERROR, message, e);
626        }
627    
628    
629        ProtocolOp protocolOp;
630        try
631        {
632          protocolOp = ProtocolOp.decode(elements.get(1));
633        }
634        catch (Exception e)
635        {
636          if (debugEnabled())
637          {
638            TRACER.debugCaught(DebugLogLevel.ERROR, e);
639          }
640    
641          Message message =
642              ERR_LDAP_MESSAGE_DECODE_PROTOCOL_OP.get(String.valueOf(e));
643          throw new LDAPException(PROTOCOL_ERROR, message, e);
644        }
645    
646    
647        ArrayList<LDAPControl> controls;
648        if (numElements == 3)
649        {
650          try
651          {
652            controls = LDAPControl.decodeControls(elements.get(2));
653          }
654          catch (Exception e)
655          {
656            if (debugEnabled())
657            {
658              TRACER.debugCaught(DebugLogLevel.ERROR, e);
659            }
660    
661            Message message =
662                ERR_LDAP_MESSAGE_DECODE_CONTROLS.get(String.valueOf(e));
663            throw new LDAPException(PROTOCOL_ERROR, message, e);
664          }
665        }
666        else
667        {
668          controls = new ArrayList<LDAPControl>(0);
669        }
670    
671    
672        return new LDAPMessage(messageID, protocolOp, controls);
673      }
674    
675    
676    
677      /**
678       * Retrieves the name of the protocol associated with this protocol element.
679       *
680       * @return  The name of the protocol associated with this protocol element.
681       */
682      public String getProtocolElementName()
683      {
684        return "LDAP";
685      }
686    
687    
688    
689      /**
690       * Retrieves a string representation of this LDAP message.
691       *
692       * @return  A string representation of this LDAP message.
693       */
694      public String toString()
695      {
696        StringBuilder buffer = new StringBuilder();
697        toString(buffer);
698        return buffer.toString();
699      }
700    
701    
702    
703      /**
704       * Appends a string representation of this protocol element to the provided
705       * buffer.
706       *
707       * @param  buffer  The buffer into which the string representation should be
708       *                 written.
709       */
710      public void toString(StringBuilder buffer)
711      {
712        buffer.append("LDAPMessage(msgID=");
713        buffer.append(messageID);
714        buffer.append(", protocolOp=");
715        if (protocolOp != null) {
716          protocolOp.toString(buffer);
717        } else {
718          buffer.append("null");
719        }
720    
721        if (controls != null && !controls.isEmpty())
722        {
723          buffer.append(", controls={ ");
724    
725          Iterator<LDAPControl> iterator = controls.iterator();
726          iterator.next().toString(buffer);
727    
728          while (iterator.hasNext())
729          {
730            buffer.append(", ");
731            iterator.next().toString(buffer);
732          }
733    
734          buffer.append(" }");
735        }
736    
737        buffer.append(")");
738      }
739    
740    
741    
742      /**
743       * Appends a string representation of this protocol element to the provided
744       * buffer.
745       *
746       * @param  buffer  The buffer into which the string representation should be
747       *                 written.
748       * @param  indent  The number of spaces that should be used to indent the
749       *                 resulting string representation.
750       */
751      public void toString(StringBuilder buffer, int indent)
752      {
753        StringBuilder indentBuf = new StringBuilder(indent);
754        for (int i=0 ; i < indent; i++)
755        {
756          indentBuf.append(' ');
757        }
758    
759        buffer.append(indentBuf);
760        buffer.append("LDAP Message");
761        buffer.append(EOL);
762    
763        buffer.append(indentBuf);
764        buffer.append("  Message ID:  ");
765        buffer.append(messageID);
766        buffer.append(EOL);
767    
768        buffer.append(indentBuf);
769        buffer.append("  Protocol Op:");
770        buffer.append(EOL);
771        protocolOp.toString(buffer, indent+4);
772    
773        if (! controls.isEmpty())
774        {
775          buffer.append(indentBuf);
776          buffer.append("  Controls:");
777    
778          for (LDAPControl c : controls)
779          {
780            c.toString(buffer, indent+4);
781          }
782        }
783      }
784    }
785