001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.shared.ldap.codec.modify; 021 022 023 import java.nio.BufferOverflowException; 024 import java.nio.ByteBuffer; 025 import java.util.ArrayList; 026 import java.util.LinkedList; 027 import java.util.List; 028 029 import org.apache.directory.shared.asn1.ber.tlv.TLV; 030 import org.apache.directory.shared.asn1.ber.tlv.UniversalTag; 031 import org.apache.directory.shared.asn1.ber.tlv.Value; 032 import org.apache.directory.shared.asn1.codec.EncoderException; 033 import org.apache.directory.shared.i18n.I18n; 034 import org.apache.directory.shared.ldap.codec.LdapConstants; 035 import org.apache.directory.shared.ldap.codec.LdapMessageCodec; 036 import org.apache.directory.shared.ldap.codec.MessageTypeEnum; 037 import org.apache.directory.shared.ldap.entry.EntryAttribute; 038 import org.apache.directory.shared.ldap.entry.Modification; 039 import org.apache.directory.shared.ldap.entry.ModificationOperation; 040 import org.apache.directory.shared.ldap.entry.client.ClientModification; 041 import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute; 042 import org.apache.directory.shared.ldap.name.DN; 043 import org.slf4j.Logger; 044 import org.slf4j.LoggerFactory; 045 046 047 /** 048 * A ModifyRequest Message. 049 * 050 * Its syntax is : 051 * 052 * ModifyRequest ::= [APPLICATION 6] SEQUENCE { 053 * object LDAPDN, 054 * modification SEQUENCE OF SEQUENCE { 055 * operation ENUMERATED { 056 * add (0), 057 * delete (1), 058 * replace (2) 059 * }, 060 * modification AttributeTypeAndValues 061 * } 062 * } 063 * 064 * AttributeTypeAndValues ::= SEQUENCE { 065 * type AttributeDescription, 066 * vals SET OF AttributeValue 067 * } 068 * 069 * AttributeValue ::= OCTET STRING 070 * 071 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 072 * @version $Rev: 918756 $, $Date: 2010-03-04 00:05:29 +0100 (Thu, 04 Mar 2010) $, 073 */ 074 public class ModifyRequestCodec extends LdapMessageCodec 075 { 076 // ~ Static fields/initializers 077 // ----------------------------------------------------------------- 078 079 /** The logger */ 080 private static final Logger LOG = LoggerFactory.getLogger( ModifyRequestCodec.class ); 081 082 // ~ Instance fields 083 // ---------------------------------------------------------------------------- 084 085 /** The DN to be modified. */ 086 private DN object; 087 088 /** The modifications list. This is an array of Modification. */ 089 private List<Modification> modifications; 090 091 /** The current attribute being decoded */ 092 private EntryAttribute currentAttribute; 093 094 /** A local storage for the operation */ 095 private ModificationOperation currentOperation; 096 097 /** The modify request length */ 098 private int modifyRequestLength; 099 100 /** The modifications length */ 101 private int modificationsLength; 102 103 /** The modification sequence length */ 104 private List<Integer> modificationSequenceLength; 105 106 /** The list of all modification length */ 107 private List<Integer> modificationLength; 108 109 /** The list of all vals length */ 110 private List<Integer> valuesLength; 111 112 113 // ~ Constructors 114 // ------------------------------------------------------------------------------- 115 116 /** 117 * Creates a new ModifyRequest object. 118 */ 119 public ModifyRequestCodec() 120 { 121 super(); 122 } 123 124 125 // ~ Methods 126 // ------------------------------------------------------------------------------------ 127 128 /** 129 * Get the message type 130 * 131 * @return Returns the type. 132 */ 133 public MessageTypeEnum getMessageType() 134 { 135 return MessageTypeEnum.MODIFY_REQUEST; 136 } 137 138 139 /** 140 * {@inheritDoc} 141 */ 142 public String getMessageTypeName() 143 { 144 return "MODIFY_REQUEST"; 145 } 146 147 148 /** 149 * Initialize the ArrayList for modifications. 150 */ 151 public void initModifications() 152 { 153 modifications = new ArrayList<Modification>(); 154 } 155 156 157 /** 158 * Get the entry's attributes 159 * 160 * @return Returns the modifications. 161 */ 162 public List<Modification> getModifications() 163 { 164 return modifications; 165 } 166 167 168 /** 169 * Add a new modification to the list 170 * 171 * @param operation The type of operation (add, delete or replace) 172 */ 173 public void addModification( int operation ) 174 { 175 currentOperation = ModificationOperation.getOperation( operation ); 176 177 if ( currentAttribute == null ) 178 { 179 modifications = new ArrayList<Modification>(); 180 } 181 } 182 183 184 /** 185 * Add a new attributeTypeAndValue 186 * 187 * @param type The attribute's name 188 */ 189 public void addAttributeTypeAndValues( String type ) 190 { 191 currentAttribute = new DefaultClientAttribute( type ); 192 193 Modification modification = new ClientModification( currentOperation, currentAttribute ); 194 modifications.add( modification ); 195 } 196 197 198 /** 199 * Add a new value to the current attribute 200 * 201 * @param value The value to add 202 */ 203 public void addAttributeValue( String value ) 204 { 205 currentAttribute.add( value ); 206 } 207 208 209 /** 210 * Add a new value to the current attribute 211 * 212 * @param value The value to add 213 */ 214 public void addAttributeValue( org.apache.directory.shared.ldap.entry.Value<?> value ) 215 { 216 currentAttribute.add( value ); 217 } 218 219 220 /** 221 * Add a new value to the current attribute 222 * 223 * @param value The value to add 224 */ 225 public void addAttributeValue( byte[] value ) 226 { 227 currentAttribute.add( value ); 228 } 229 230 231 /** 232 * Return the current attribute's type 233 */ 234 public String getCurrentAttributeType() 235 { 236 return currentAttribute.getId(); 237 } 238 239 240 /** 241 * Get the modification's DN 242 * 243 * @return Returns the object. 244 */ 245 public DN getObject() 246 { 247 return object; 248 } 249 250 251 /** 252 * Set the modification DN. 253 * 254 * @param object The DN to set. 255 */ 256 public void setObject( DN object ) 257 { 258 this.object = object; 259 } 260 261 262 /** 263 * Get the current operation 264 * 265 * @return Returns the currentOperation. 266 */ 267 public int getCurrentOperation() 268 { 269 return currentOperation.getValue(); 270 } 271 272 273 /** 274 * Store the current operation 275 * 276 * @param currentOperation The currentOperation to set. 277 */ 278 public void setCurrentOperation( int currentOperation ) 279 { 280 this.currentOperation = ModificationOperation.getOperation( currentOperation ); 281 } 282 283 284 /** 285 * Store the current operation 286 * 287 * @param currentOperation The currentOperation to set. 288 */ 289 public void setCurrentOperation( ModificationOperation currentOperation ) 290 { 291 this.currentOperation = currentOperation; 292 } 293 294 295 /** 296 * sets the modifications 297 * 298 * @param modifications the list of modifications 299 */ 300 public void setModifications( List<Modification> modifications ) 301 { 302 this.modifications = modifications; 303 } 304 305 306 /** 307 * Compute the ModifyRequest length 308 * 309 * ModifyRequest : 310 * 311 * 0x66 L1 312 * | 313 * +--> 0x04 L2 object 314 * +--> 0x30 L3 modifications 315 * | 316 * +--> 0x30 L4-1 modification sequence 317 * | | 318 * | +--> 0x0A 0x01 (0..2) operation 319 * | +--> 0x30 L5-1 modification 320 * | | 321 * | +--> 0x04 L6-1 type 322 * | +--> 0x31 L7-1 vals 323 * | | 324 * | +--> 0x04 L8-1-1 attributeValue 325 * | +--> 0x04 L8-1-2 attributeValue 326 * | +--> ... 327 * | +--> 0x04 L8-1-i attributeValue 328 * | +--> ... 329 * | +--> 0x04 L8-1-n attributeValue 330 * | 331 * +--> 0x30 L4-2 modification sequence 332 * . | 333 * . +--> 0x0A 0x01 (0..2) operation 334 * . +--> 0x30 L5-2 modification 335 * | 336 * +--> 0x04 L6-2 type 337 * +--> 0x31 L7-2 vals 338 * | 339 * +--> 0x04 L8-2-1 attributeValue 340 * +--> 0x04 L8-2-2 attributeValue 341 * +--> ... 342 * +--> 0x04 L8-2-i attributeValue 343 * +--> ... 344 * +--> 0x04 L8-2-n attributeValue 345 */ 346 protected int computeLengthProtocolOp() 347 { 348 // Initialized with object 349 modifyRequestLength = 1 + TLV.getNbBytes( DN.getNbBytes( object ) ) + DN.getNbBytes( object ); 350 351 // Modifications 352 modificationsLength = 0; 353 354 if ( ( modifications != null ) && ( modifications.size() != 0 ) ) 355 { 356 modificationSequenceLength = new LinkedList<Integer>(); 357 modificationLength = new LinkedList<Integer>(); 358 valuesLength = new LinkedList<Integer>(); 359 360 for ( Modification modification:modifications ) 361 { 362 // Modification sequence length initialized with the operation 363 int localModificationSequenceLength = 1 + 1 + 1; 364 int localValuesLength = 0; 365 366 // Modification length initialized with the type 367 int typeLength = modification.getAttribute().getId().length(); 368 int localModificationLength = 1 + TLV.getNbBytes( typeLength ) + typeLength; 369 370 // Get all the values 371 if ( modification.getAttribute().size() != 0 ) 372 { 373 for ( org.apache.directory.shared.ldap.entry.Value<?> value:modification.getAttribute() ) 374 { 375 localValuesLength += 1 + TLV.getNbBytes( value.getBytes().length ) 376 + value.getBytes().length; 377 } 378 } 379 380 localModificationLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength; 381 382 // Compute the modificationSequenceLength 383 localModificationSequenceLength += 1 + TLV.getNbBytes( localModificationLength ) 384 + localModificationLength; 385 386 // Add the tag and the length 387 modificationsLength += 1 + TLV.getNbBytes( localModificationSequenceLength ) 388 + localModificationSequenceLength; 389 390 // Store the arrays of values 391 valuesLength.add( localValuesLength ); 392 modificationLength.add( localModificationLength ); 393 modificationSequenceLength.add( localModificationSequenceLength ); 394 } 395 396 // Add the modifications length to the modificationRequestLength 397 modifyRequestLength += 1 + TLV.getNbBytes( modificationsLength ) + modificationsLength; 398 } 399 400 return 1 + TLV.getNbBytes( modifyRequestLength ) + modifyRequestLength; 401 } 402 403 404 /** 405 * Encode the ModifyRequest message to a PDU. 406 * 407 * ModifyRequest : 408 * <pre> 409 * 0x66 LL 410 * 0x04 LL object 411 * 0x30 LL modifiations 412 * 0x30 LL modification sequence 413 * 0x0A 0x01 operation 414 * 0x30 LL modification 415 * 0x04 LL type 416 * 0x31 LL vals 417 * 0x04 LL attributeValue 418 * ... 419 * 0x04 LL attributeValue 420 * ... 421 * 0x30 LL modification sequence 422 * 0x0A 0x01 operation 423 * 0x30 LL modification 424 * 0x04 LL type 425 * 0x31 LL vals 426 * 0x04 LL attributeValue 427 * ... 428 * 0x04 LL attributeValue 429 * </pre> 430 * 431 * @param buffer The buffer where to put the PDU 432 * @return The PDU. 433 */ 434 protected void encodeProtocolOp( ByteBuffer buffer ) throws EncoderException 435 { 436 try 437 { 438 // The AddRequest Tag 439 buffer.put( LdapConstants.MODIFY_REQUEST_TAG ); 440 buffer.put( TLV.getBytes( modifyRequestLength ) ); 441 442 // The entry 443 Value.encode( buffer, DN.getBytes( object ) ); 444 445 // The modifications sequence 446 buffer.put( UniversalTag.SEQUENCE_TAG ); 447 buffer.put( TLV.getBytes( modificationsLength ) ); 448 449 // The modifications list 450 if ( ( modifications != null ) && ( modifications.size() != 0 ) ) 451 { 452 int modificationNumber = 0; 453 454 // Compute the modifications length 455 for ( Modification modification:modifications ) 456 { 457 // The modification sequence 458 buffer.put( UniversalTag.SEQUENCE_TAG ); 459 int localModificationSequenceLength = modificationSequenceLength 460 .get( modificationNumber ); 461 buffer.put( TLV.getBytes( localModificationSequenceLength ) ); 462 463 // The operation. The value has to be changed, it's not 464 // the same value in DirContext and in RFC 2251. 465 buffer.put( UniversalTag.ENUMERATED_TAG ); 466 buffer.put( ( byte ) 1 ); 467 buffer.put( ( byte ) modification.getOperation().getValue() ); 468 469 // The modification 470 buffer.put( UniversalTag.SEQUENCE_TAG ); 471 int localModificationLength = modificationLength.get( modificationNumber ); 472 buffer.put( TLV.getBytes( localModificationLength ) ); 473 474 // The modification type 475 Value.encode( buffer, modification.getAttribute().getId() ); 476 477 // The values 478 buffer.put( UniversalTag.SET_TAG ); 479 int localValuesLength = valuesLength.get( modificationNumber ); 480 buffer.put( TLV.getBytes( localValuesLength ) ); 481 482 if ( modification.getAttribute().size() != 0 ) 483 { 484 for ( org.apache.directory.shared.ldap.entry.Value<?> value:modification.getAttribute() ) 485 { 486 if ( !value.isBinary() ) 487 { 488 Value.encode( buffer, value.getString() ); 489 } 490 else 491 { 492 Value.encode( buffer, value.getBytes() ); 493 } 494 } 495 } 496 497 // Go to the next modification number; 498 modificationNumber++; 499 } 500 } 501 } 502 catch ( BufferOverflowException boe ) 503 { 504 throw new EncoderException( I18n.err( I18n.ERR_04005 ) ); 505 } 506 } 507 508 509 /** 510 * Get a String representation of a ModifyRequest 511 * 512 * @return A ModifyRequest String 513 */ 514 public String toString() 515 { 516 StringBuffer sb = new StringBuffer(); 517 518 sb.append( " Modify Request\n" ); 519 sb.append( " Object : '" ).append( object ).append( "'\n" ); 520 521 if ( modifications != null ) 522 { 523 int i = 0; 524 525 for ( Modification modification:modifications ) 526 { 527 sb.append( " Modification[" ).append( i ).append( "]\n" ); 528 sb.append( " Operation : " ); 529 530 if ( modification != null ) 531 { 532 switch ( modification.getOperation() ) 533 { 534 535 case ADD_ATTRIBUTE: 536 sb.append( " add\n" ); 537 break; 538 539 case REPLACE_ATTRIBUTE: 540 sb.append( " replace\n" ); 541 break; 542 543 case REMOVE_ATTRIBUTE: 544 sb.append( " delete\n" ); 545 break; 546 } 547 548 sb.append( " Modification\n" ); 549 550 EntryAttribute attribute = modification.getAttribute(); 551 552 if ( attribute != null ) 553 { 554 sb.append( attribute ); 555 } 556 } 557 else 558 { 559 sb.append( " unknown modification operation\n" ); 560 } 561 562 } 563 } 564 565 return sb.toString(); 566 } 567 }