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    import java.util.List;
035    
036    import org.opends.server.protocols.asn1.ASN1Element;
037    import org.opends.server.protocols.asn1.ASN1OctetString;
038    import org.opends.server.protocols.asn1.ASN1Sequence;
039    import org.opends.server.types.DebugLogLevel;
040    import org.opends.server.types.LDAPException;
041    import org.opends.server.types.RawAttribute;
042    import org.opends.server.util.Base64;
043    
044    
045    import static org.opends.server.loggers.debug.DebugLogger.*;
046    import org.opends.server.loggers.debug.DebugTracer;
047    import static org.opends.messages.ProtocolMessages.*;
048    import static org.opends.server.protocols.ldap.LDAPConstants.*;
049    import static org.opends.server.protocols.ldap.LDAPResultCode.*;
050    import static org.opends.server.util.ServerConstants.*;
051    import static org.opends.server.util.StaticUtils.*;
052    
053    
054    
055    /**
056     * This class defines the structures and methods for an LDAP add request
057     * protocol op, which is used to add a new entry to the Directory Server.
058     */
059    public class AddRequestProtocolOp
060           extends ProtocolOp
061    {
062      /**
063       * The tracer object for the debug logger.
064       */
065      private static final DebugTracer TRACER = getTracer();
066    
067      // The set of attributes for this add request.
068      private List<RawAttribute> attributes;
069    
070      // The DN for this add request.
071      private ASN1OctetString dn;
072    
073    
074    
075      /**
076       * Creates a new LDAP add request protocol op with the specified DN and no
077       * attributes.
078       *
079       * @param  dn  The DN for this add request.
080       */
081      public AddRequestProtocolOp(ASN1OctetString dn)
082      {
083        this.dn         = dn;
084        this.attributes = new ArrayList<RawAttribute>();
085      }
086    
087    
088    
089      /**
090       * Creates a new LDAP add request protocol op with the specified DN and set of
091       * attributes.
092       *
093       * @param  dn          The DN for this add request.
094       * @param  attributes  The set of attributes for this add request.
095       */
096      public AddRequestProtocolOp(ASN1OctetString dn,
097                                  ArrayList<RawAttribute> attributes)
098      {
099        this.dn = dn;
100    
101        if (attributes == null)
102        {
103          this.attributes = new ArrayList<RawAttribute>();
104        }
105        else
106        {
107          this.attributes = attributes;
108        }
109      }
110    
111    
112    
113      /**
114       * Retrieves the DN for this add request.
115       *
116       * @return  The DN for this add request.
117       */
118      public ASN1OctetString getDN()
119      {
120        return dn;
121      }
122    
123    
124    
125      /**
126       * Specifies the DN for this add request.
127       *
128       * @param  dn  The DN for this add request.
129       */
130      public void setDN(ASN1OctetString dn)
131      {
132        this.dn = dn;
133      }
134    
135    
136    
137      /**
138       * Retrieves the set of attributes for this add request.  The returned list
139       * may be altered by the caller.
140       *
141       * @return  The set of attributes for this add request.
142       */
143      public List<RawAttribute> getAttributes()
144      {
145        return attributes;
146      }
147    
148    
149    
150      /**
151       * Retrieves the BER type for this protocol op.
152       *
153       * @return  The BER type for this protocol op.
154       */
155      public byte getType()
156      {
157        return OP_TYPE_ADD_REQUEST;
158      }
159    
160    
161    
162      /**
163       * Retrieves the name for this protocol op type.
164       *
165       * @return  The name for this protocol op type.
166       */
167      public String getProtocolOpName()
168      {
169        return "Add Request";
170      }
171    
172    
173    
174      /**
175       * Encodes this protocol op to an ASN.1 element suitable for including in an
176       * LDAP message.
177       *
178       * @return  The ASN.1 element containing the encoded protocol op.
179       */
180      public ASN1Element encode()
181      {
182        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
183        elements.add(dn);
184    
185    
186        ArrayList<ASN1Element> attrElements =
187             new ArrayList<ASN1Element>(attributes.size());
188        for (RawAttribute attr : attributes)
189        {
190          attrElements.add(attr.encode());
191        }
192        elements.add(new ASN1Sequence(attrElements));
193    
194    
195        return new ASN1Sequence(OP_TYPE_ADD_REQUEST, elements);
196      }
197    
198    
199    
200      /**
201       * Decodes the provided ASN.1 element as an LDAP add request protocol op.
202       *
203       * @param  element  The ASN.1 element to be decoded.
204       *
205       * @return  The decoded add request protocol op.
206       *
207       * @throws  LDAPException  If a problem occurs while decoding the provided
208       *                         ASN.1 element as an LDAP add request protocol op.
209       */
210      public static AddRequestProtocolOp decodeAddRequest(ASN1Element element)
211             throws LDAPException
212      {
213        ArrayList<ASN1Element> elements;
214        try
215        {
216          elements = element.decodeAsSequence().elements();
217        }
218        catch (Exception e)
219        {
220          if (debugEnabled())
221          {
222            TRACER.debugCaught(DebugLogLevel.ERROR, e);
223          }
224    
225          Message message =
226              ERR_LDAP_ADD_REQUEST_DECODE_SEQUENCE.get(String.valueOf(e));
227          throw new LDAPException(PROTOCOL_ERROR, message, e);
228        }
229    
230    
231        int numElements = elements.size();
232        if (numElements != 2)
233        {
234          Message message =
235              ERR_LDAP_ADD_REQUEST_DECODE_INVALID_ELEMENT_COUNT.get(numElements);
236          throw new LDAPException(PROTOCOL_ERROR, message);
237        }
238    
239    
240        ASN1OctetString dn;
241        try
242        {
243          dn = elements.get(0).decodeAsOctetString();
244        }
245        catch (Exception e)
246        {
247          if (debugEnabled())
248          {
249            TRACER.debugCaught(DebugLogLevel.ERROR, e);
250          }
251    
252          Message message = ERR_LDAP_ADD_REQUEST_DECODE_DN.get(String.valueOf(e));
253          throw new LDAPException(PROTOCOL_ERROR, message, e);
254        }
255    
256    
257    
258        ArrayList<RawAttribute> attributes;
259        try
260        {
261          ArrayList<ASN1Element> attrElements =
262               elements.get(1).decodeAsSequence().elements();
263          attributes = new ArrayList<RawAttribute>(attrElements.size());
264          for (ASN1Element e : attrElements)
265          {
266            attributes.add(LDAPAttribute.decode(e));
267          }
268        }
269        catch (Exception e)
270        {
271          if (debugEnabled())
272          {
273            TRACER.debugCaught(DebugLogLevel.ERROR, e);
274          }
275    
276          Message message =
277              ERR_LDAP_ADD_REQUEST_DECODE_ATTRS.get(String.valueOf(e));
278          throw new LDAPException(PROTOCOL_ERROR, message, e);
279        }
280    
281    
282        return new AddRequestProtocolOp(dn, attributes);
283      }
284    
285    
286    
287      /**
288       * Appends a string representation of this LDAP protocol op to the provided
289       * buffer.
290       *
291       * @param  buffer  The buffer to which the string should be appended.
292       */
293      public void toString(StringBuilder buffer)
294      {
295        buffer.append("AddRequest(dn=");
296        dn.toString(buffer);
297        buffer.append(", attrs={");
298    
299        if (! attributes.isEmpty())
300        {
301          Iterator<RawAttribute> iterator = attributes.iterator();
302          iterator.next().toString(buffer);
303    
304          while (iterator.hasNext())
305          {
306            buffer.append(", ");
307            iterator.next().toString(buffer);
308          }
309        }
310    
311        buffer.append("})");
312      }
313    
314    
315    
316      /**
317       * Appends a multi-line string representation of this LDAP protocol op to the
318       * provided buffer.
319       *
320       * @param  buffer  The buffer to which the information should be appended.
321       * @param  indent  The number of spaces from the margin that the lines should
322       *                 be indented.
323       */
324      public void toString(StringBuilder buffer, int indent)
325      {
326        StringBuilder indentBuf = new StringBuilder(indent);
327        for (int i=0 ; i < indent; i++)
328        {
329          indentBuf.append(' ');
330        }
331    
332        buffer.append(indentBuf);
333        buffer.append("Add Request");
334        buffer.append(EOL);
335    
336        buffer.append(indentBuf);
337        buffer.append("  DN:  ");
338        dn.toString(buffer);
339        buffer.append(EOL);
340    
341        buffer.append("  Attributes:");
342        buffer.append(EOL);
343    
344        for (RawAttribute attribute : attributes)
345        {
346          attribute.toString(buffer, indent+4);
347        }
348      }
349    
350    
351    
352      /**
353       * Appends an LDIF representation of the entry to the provided buffer.
354       *
355       * @param  buffer      The buffer to which the entry should be appended.
356       * @param  wrapColumn  The column at which long lines should be wrapped.
357       */
358      public void toLDIF(StringBuilder buffer, int wrapColumn)
359      {
360        // Add the DN to the buffer.
361        String dnString;
362        int    colsRemaining;
363        if (needsBase64Encoding(dn.value()))
364        {
365          dnString = Base64.encode(dn.value());
366          buffer.append("dn:: ");
367    
368          colsRemaining = wrapColumn - 5;
369        }
370        else
371        {
372          dnString = dn.stringValue();
373          buffer.append("dn: ");
374    
375          colsRemaining = wrapColumn - 4;
376        }
377    
378        int dnLength = dnString.length();
379        if ((dnLength <= colsRemaining) || (colsRemaining <= 0))
380        {
381          buffer.append(dnString);
382          buffer.append(EOL);
383        }
384        else
385        {
386          buffer.append(dnString.substring(0, colsRemaining));
387          buffer.append(EOL);
388    
389          int startPos = colsRemaining;
390          while ((dnLength - startPos) > (wrapColumn - 1))
391          {
392            buffer.append(" ");
393            buffer.append(dnString.substring(startPos, (startPos+wrapColumn-1)));
394            buffer.append(EOL);
395    
396            startPos += (wrapColumn-1);
397          }
398    
399          if (startPos < dnLength)
400          {
401            buffer.append(" ");
402            buffer.append(dnString.substring(startPos));
403            buffer.append(EOL);
404          }
405        }
406    
407    
408        // Add the attributes to the buffer.
409        for (RawAttribute a : attributes)
410        {
411          String name       = a.getAttributeType();
412          int    nameLength = name.length();
413    
414          for (ASN1OctetString v : a.getValues())
415          {
416            String valueString;
417            if (needsBase64Encoding(v.value()))
418            {
419              valueString = Base64.encode(v.value());
420              buffer.append(name);
421              buffer.append(":: ");
422    
423              colsRemaining = wrapColumn - nameLength - 3;
424            }
425            else
426            {
427              valueString = v.stringValue();
428              buffer.append(name);
429              buffer.append(": ");
430    
431              colsRemaining = wrapColumn - nameLength - 2;
432            }
433    
434            int valueLength = valueString.length();
435            if ((valueLength <= colsRemaining) || (colsRemaining <= 0))
436            {
437              buffer.append(valueString);
438              buffer.append(EOL);
439            }
440            else
441            {
442              buffer.append(valueString.substring(0, colsRemaining));
443              buffer.append(EOL);
444    
445              int startPos = colsRemaining;
446              while ((valueLength - startPos) > (wrapColumn - 1))
447              {
448                buffer.append(" ");
449                buffer.append(valueString.substring(startPos,
450                                                    (startPos+wrapColumn-1)));
451                buffer.append(EOL);
452    
453                startPos += (wrapColumn-1);
454              }
455    
456              if (startPos < valueLength)
457              {
458                buffer.append(" ");
459                buffer.append(valueString.substring(startPos));
460                buffer.append(EOL);
461              }
462            }
463          }
464        }
465    
466    
467        // Make sure to add an extra blank line to ensure that there will be one
468        // between this entry and the next.
469        buffer.append(EOL);
470      }
471    }
472