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    
034    import org.opends.server.protocols.asn1.ASN1Boolean;
035    import org.opends.server.protocols.asn1.ASN1Element;
036    import org.opends.server.protocols.asn1.ASN1OctetString;
037    import org.opends.server.protocols.asn1.ASN1Sequence;
038    import org.opends.server.types.Control;
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.asn1.ASN1Constants.*;
046    import static org.opends.server.protocols.ldap.LDAPConstants.*;
047    import static org.opends.server.protocols.ldap.LDAPResultCode.*;
048    import static org.opends.server.util.ServerConstants.*;
049    
050    
051    
052    /**
053     * This class defines the data structures and methods to use when interacting
054     * with a generic LDAP control or set of controls.
055     */
056    public class LDAPControl
057    {
058      /**
059       * The tracer object for the debug logger.
060       */
061      private static final DebugTracer TRACER = getTracer();
062    
063      // The control wrapped by this LDAP control.
064      private Control control;
065    
066    
067    
068      /**
069       * Creates a new LDAP control with the information in the provided control.
070       *
071       * @param  control  The control to use to create this LDAP control.
072       */
073      public LDAPControl(Control control)
074      {
075        this.control = control;
076      }
077    
078    
079    
080      /**
081       * Creates a new LDAP control with the specified OID.  It will not be
082       * critical, and will not have a value.
083       *
084       * @param  oid  The OID for this LDAP control.
085       */
086      public LDAPControl(String oid)
087      {
088        control = new Control(oid, false);
089      }
090    
091    
092    
093      /**
094       * Creates a new LDAP control with the specified OID and criticality.  It will
095       * not have a value.
096       *
097       * @param  oid         The OID for this LDAP control.
098       * @param  isCritical  Indicates whether this control should be considered
099       *                     critical.
100       */
101      public LDAPControl(String oid, boolean isCritical)
102      {
103        control = new Control(oid, isCritical);
104      }
105    
106    
107    
108      /**
109       * Creates a new LDAP control with the specified OID, criticality, and value.
110       *
111       * @param  oid         The OID for this LDAP control.
112       * @param  isCritical  Indicates whether this control should be considered
113       *                     critical.
114       * @param  value       The value for this LDAP control.
115       */
116      public LDAPControl(String oid, boolean isCritical, ASN1OctetString value)
117      {
118        control = new Control(oid, isCritical, value);
119      }
120    
121    
122    
123      /**
124       * Retrieves the control wrapped by this LDAP control.
125       *
126       * @return  The control wrapped by this LDAP control.
127       */
128      public Control getControl()
129      {
130        return control;
131      }
132    
133    
134    
135      /**
136       * Encodes this control to an ASN.1 element.
137       *
138       * @return  The ASN.1 element containing the encoded control.
139       */
140      public ASN1Element encode()
141      {
142        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
143        elements.add(new ASN1OctetString(control.getOID()));
144    
145        if (control.isCritical())
146        {
147          elements.add(new ASN1Boolean(control.isCritical()));
148        }
149    
150        ASN1OctetString value = control.getValue();
151        if (value != null)
152        {
153          elements.add(value);
154        }
155    
156        return new ASN1Sequence(elements);
157      }
158    
159    
160    
161      /**
162       * Encodes the provided set of controls into an ASN.1 sequence.
163       *
164       * @param  controls  The set of controls to encode.
165       *
166       * @return  The ASN.1 element containing the encoded set of controls.
167       */
168      public static ASN1Element encodeControls(ArrayList<LDAPControl> controls)
169      {
170        ArrayList<ASN1Element> elements =
171             new ArrayList<ASN1Element>(controls.size());
172        for (LDAPControl c : controls)
173        {
174          elements.add(c.encode());
175        }
176    
177        return new ASN1Sequence(TYPE_CONTROL_SEQUENCE, elements);
178      }
179    
180    
181    
182      /**
183       * Decodes the provided ASN.1 element as an LDAP control.
184       *
185       * @param  element  The ASN.1 element to decode.
186       *
187       * @return  The decoded LDAP control.
188       *
189       * @throws  LDAPException  If a problem occurs while attempting to decode the
190       *                         provided ASN.1 element as an LDAP control.
191       */
192      public static LDAPControl decode(ASN1Element element)
193             throws LDAPException
194      {
195        if (element == null)
196        {
197          Message message = ERR_LDAP_CONTROL_DECODE_NULL.get();
198          throw new LDAPException(PROTOCOL_ERROR, message);
199        }
200    
201    
202        ArrayList<ASN1Element> elements;
203        try
204        {
205          elements = element.decodeAsSequence().elements();
206        }
207        catch (Exception e)
208        {
209          if (debugEnabled())
210          {
211            TRACER.debugCaught(DebugLogLevel.ERROR, e);
212          }
213    
214          Message message = ERR_LDAP_CONTROL_DECODE_SEQUENCE.get(String.valueOf(e));
215          throw new LDAPException(PROTOCOL_ERROR, message, e);
216        }
217    
218    
219        int numElements = elements.size();
220        if ((numElements < 1) || (numElements > 3))
221        {
222          Message message =
223              ERR_LDAP_CONTROL_DECODE_INVALID_ELEMENT_COUNT.get(numElements);
224          throw new LDAPException(PROTOCOL_ERROR, message);
225        }
226    
227    
228        String oid;
229        try
230        {
231          oid = elements.get(0).decodeAsOctetString().stringValue();
232        }
233        catch (Exception e)
234        {
235          if (debugEnabled())
236          {
237            TRACER.debugCaught(DebugLogLevel.ERROR, e);
238          }
239    
240          Message message = ERR_LDAP_CONTROL_DECODE_OID.get(String.valueOf(e));
241          throw new LDAPException(PROTOCOL_ERROR, message, e);
242        }
243    
244    
245        if (numElements == 1)
246        {
247          return new LDAPControl(oid);
248        }
249        else if (numElements == 2)
250        {
251          boolean         isCritical;
252          ASN1OctetString value;
253    
254          ASN1Element e = elements.get(1);
255          switch (e.getType())
256          {
257            case UNIVERSAL_BOOLEAN_TYPE:
258              value = null;
259    
260              try
261              {
262                isCritical = e.decodeAsBoolean().booleanValue();
263              }
264              catch (Exception e2)
265              {
266                if (debugEnabled())
267                {
268                  TRACER.debugCaught(DebugLogLevel.ERROR, e2);
269                }
270    
271                Message message =
272                    ERR_LDAP_CONTROL_DECODE_CRITICALITY.get(String.valueOf(e));
273                throw new LDAPException(PROTOCOL_ERROR, message, e2);
274              }
275              break;
276            case UNIVERSAL_OCTET_STRING_TYPE:
277              isCritical = false;
278    
279              try
280              {
281                value = e.decodeAsOctetString();
282              }
283              catch (Exception e2)
284              {
285                if (debugEnabled())
286                {
287                  TRACER.debugCaught(DebugLogLevel.ERROR, e2);
288                }
289    
290                Message message =
291                    ERR_LDAP_CONTROL_DECODE_VALUE.get(String.valueOf(e));
292                throw new LDAPException(PROTOCOL_ERROR, message, e2);
293              }
294              break;
295            default:
296              Message message =
297                  ERR_LDAP_CONTROL_DECODE_INVALID_TYPE.get(e.getType());
298              throw new LDAPException(PROTOCOL_ERROR, message);
299          }
300    
301          return new LDAPControl(oid, isCritical, value);
302        }
303        else
304        {
305          boolean isCritical;
306          try
307          {
308            isCritical = elements.get(1).decodeAsBoolean().booleanValue();
309          }
310          catch (Exception e)
311          {
312            if (debugEnabled())
313            {
314              TRACER.debugCaught(DebugLogLevel.ERROR, e);
315            }
316    
317            Message message =
318                ERR_LDAP_CONTROL_DECODE_CRITICALITY.get(String.valueOf(e));
319            throw new LDAPException(PROTOCOL_ERROR, message, e);
320          }
321    
322          ASN1OctetString value;
323          try
324          {
325            value = elements.get(2).decodeAsOctetString();
326          }
327          catch (Exception e)
328          {
329            if (debugEnabled())
330            {
331              TRACER.debugCaught(DebugLogLevel.ERROR, e);
332            }
333    
334            Message message = ERR_LDAP_CONTROL_DECODE_VALUE.get(String.valueOf(e));
335            throw new LDAPException(PROTOCOL_ERROR, message, e);
336          }
337    
338          return new LDAPControl(oid, isCritical, value);
339        }
340      }
341    
342    
343    
344      /**
345       * Decodes the provided ASN.1 element as a set of controls.
346       *
347       * @param  element  The ASN.1 element containing the encoded set of controls.
348       *
349       * @return  The decoded set of controls.
350       *
351       * @throws  LDAPException  If a problem occurs while attempting to decode the
352       *                         controls.
353       */
354      public static ArrayList<LDAPControl> decodeControls(ASN1Element element)
355             throws LDAPException
356      {
357        if (element == null)
358        {
359          Message message = ERR_LDAP_CONTROL_DECODE_CONTROLS_NULL.get();
360          throw new LDAPException(PROTOCOL_ERROR, message);
361        }
362    
363    
364        ArrayList<ASN1Element> elements;
365        try
366        {
367          elements = element.decodeAsSequence().elements();
368        }
369        catch (Exception e)
370        {
371          Message message =
372              ERR_LDAP_CONTROL_DECODE_CONTROLS_SEQUENCE.get(String.valueOf(e));
373          throw new LDAPException(PROTOCOL_ERROR, message, e);
374        }
375    
376    
377        ArrayList<LDAPControl> controls =
378             new ArrayList<LDAPControl>(elements.size());
379        for (ASN1Element e : elements)
380        {
381          controls.add(decode(e));
382        }
383    
384        return controls;
385      }
386    
387    
388    
389      /**
390       * Retrieves the OID for this control.
391       *
392       * @return  The OID for this control.
393       */
394      public String getOID()
395      {
396        return control.getOID();
397      }
398    
399    
400    
401      /**
402       * Indicates whether this control should be considered critical.
403       *
404       * @return  <CODE>true</CODE> if this control should be considered critical,
405       *          or <CODE>false</CODE> if not.
406       */
407      public boolean isCritical()
408      {
409        return control.isCritical();
410      }
411    
412    
413    
414      /**
415       * Retrieves the value for this control.
416       *
417       * @return  The value for this control, or <CODE>null</CODE> if there is none.
418       */
419      public ASN1OctetString getValue()
420      {
421        return control.getValue();
422      }
423    
424    
425    
426      /**
427       * Retrieves a string representation of this LDAP control.
428       *
429       * @return  A string representation of this LDAP control.
430       */
431      public String toString()
432      {
433        StringBuilder buffer = new StringBuilder();
434        toString(buffer);
435        return buffer.toString();
436      }
437    
438    
439    
440      /**
441       * Appends a string representation of this LDAP control to the provided
442       * buffer.
443       *
444       * @param  buffer  The buffer to which the information should be appended.
445       */
446      public void toString(StringBuilder buffer)
447      {
448        buffer.append("LDAPControl(oid=");
449        buffer.append(control.getOID());
450        buffer.append(", criticality=");
451        buffer.append(control.isCritical());
452    
453        ASN1OctetString value = control.getValue();
454        if (value != null)
455        {
456          buffer.append(", value=");
457          buffer.append(String.valueOf(value));
458        }
459    
460        buffer.append(")");
461      }
462    
463    
464    
465      /**
466       * Appends a multi-line string representation of this LDAP control to the
467       * provided buffer.
468       *
469       * @param  buffer  The buffer to which the information should be appended.
470       * @param  indent  The number of spaces to indent the information.
471       */
472      public void toString(StringBuilder buffer, int indent)
473      {
474        StringBuilder indentBuf = new StringBuilder(indent);
475        for (int i=0 ; i < indent; i++)
476        {
477          indentBuf.append(' ');
478        }
479    
480        buffer.append(indentBuf);
481        buffer.append("LDAP Control");
482        buffer.append(EOL);
483    
484        buffer.append(indentBuf);
485        buffer.append("  OID:  ");
486        buffer.append(control.getOID());
487        buffer.append(EOL);
488    
489        buffer.append(indentBuf);
490        buffer.append("  Criticality:  ");
491        buffer.append(control.isCritical());
492        buffer.append(EOL);
493    
494        ASN1OctetString value = control.getValue();
495        if (value != null)
496        {
497          buffer.append(indentBuf);
498          buffer.append("  Value:");
499          value.toString(buffer, indent+4);
500        }
501      }
502    }
503