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