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 01:05:29 +0200 (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 }