1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.api.ldap.model.ldif;
21
22
23 import java.io.IOException;
24
25 import javax.naming.directory.Attributes;
26
27 import org.apache.directory.api.i18n.I18n;
28 import org.apache.directory.api.ldap.model.entry.Attribute;
29 import org.apache.directory.api.ldap.model.entry.AttributeUtils;
30 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
31 import org.apache.directory.api.ldap.model.entry.Entry;
32 import org.apache.directory.api.ldap.model.entry.Modification;
33 import org.apache.directory.api.ldap.model.entry.Value;
34 import org.apache.directory.api.ldap.model.exception.LdapException;
35 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
36 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
37 import org.apache.directory.api.ldap.model.name.Dn;
38 import org.apache.directory.api.util.Base64;
39 import org.apache.directory.api.util.Strings;
40
41
42
43
44
45
46
47 public final class LdifUtils
48 {
49
50 private static final boolean[] LDIF_SAFE_STARTING_CHAR_ALPHABET = new boolean[128];
51
52
53 private static final boolean[] LDIF_SAFE_OTHER_CHARS_ALPHABET = new boolean[128];
54
55
56 private static final int DEFAULT_LINE_LENGTH = 80;
57
58
59 private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
60
61 static
62 {
63
64 for ( int i = 0; i < 128; i++ )
65 {
66 LDIF_SAFE_STARTING_CHAR_ALPHABET[i] = true;
67 }
68
69
70 LDIF_SAFE_STARTING_CHAR_ALPHABET[0] = false;
71
72 LDIF_SAFE_STARTING_CHAR_ALPHABET[10] = false;
73
74 LDIF_SAFE_STARTING_CHAR_ALPHABET[13] = false;
75
76 LDIF_SAFE_STARTING_CHAR_ALPHABET[32] = false;
77
78 LDIF_SAFE_STARTING_CHAR_ALPHABET[58] = false;
79
80 LDIF_SAFE_STARTING_CHAR_ALPHABET[60] = false;
81
82
83 for ( int i = 0; i < 128; i++ )
84 {
85 LDIF_SAFE_OTHER_CHARS_ALPHABET[i] = true;
86 }
87
88
89 LDIF_SAFE_OTHER_CHARS_ALPHABET[0] = false;
90
91 LDIF_SAFE_OTHER_CHARS_ALPHABET[10] = false;
92
93 LDIF_SAFE_OTHER_CHARS_ALPHABET[13] = false;
94 }
95
96
97
98
99
100 private LdifUtils()
101 {
102 }
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 public static boolean isLDIFSafe( String str )
136 {
137 if ( Strings.isEmpty( str ) )
138 {
139
140 return true;
141 }
142
143
144 char currentChar = str.charAt( 0 );
145
146 if ( ( currentChar > 127 ) || !LDIF_SAFE_STARTING_CHAR_ALPHABET[currentChar] )
147 {
148 return false;
149 }
150
151
152 for ( int i = 1; i < str.length(); i++ )
153 {
154 currentChar = str.charAt( i );
155
156 if ( ( currentChar > 127 ) || !LDIF_SAFE_OTHER_CHARS_ALPHABET[currentChar] )
157 {
158 return false;
159 }
160 }
161
162
163 return currentChar != ' ';
164 }
165
166
167
168
169
170
171
172
173
174 public static String convertToLdif( Attributes attrs ) throws LdapException
175 {
176 return convertAttributesToLdif( AttributeUtils.toEntry( attrs, null ), DEFAULT_LINE_LENGTH );
177 }
178
179
180
181
182
183
184
185
186
187
188 public static String convertToLdif( Attributes attrs, int length ) throws LdapException
189 {
190 return convertAttributesToLdif( AttributeUtils.toEntry( attrs, null ), length );
191 }
192
193
194
195
196
197
198
199
200
201
202
203 public static String convertToLdif( Attributes attrs, Dn dn, int length ) throws LdapException
204 {
205 return convertToLdif( AttributeUtils.toEntry( attrs, dn ), length );
206 }
207
208
209
210
211
212
213
214
215
216
217 public static String convertToLdif( Attributes attrs, Dn dn ) throws LdapException
218 {
219 return convertToLdif( AttributeUtils.toEntry( attrs, dn ), DEFAULT_LINE_LENGTH );
220 }
221
222
223
224
225
226
227
228
229
230 public static String convertToLdif( Entry entry ) throws LdapException
231 {
232 return convertToLdif( entry, DEFAULT_LINE_LENGTH );
233 }
234
235
236
237
238
239
240
241
242
243
244 public static String convertToLdif( Entry entry, boolean includeVersionInfo ) throws LdapException
245 {
246 String ldif = convertToLdif( entry, DEFAULT_LINE_LENGTH );
247
248 if ( includeVersionInfo )
249 {
250 ldif = "version: 1" + LINE_SEPARATOR + ldif;
251 }
252
253 return ldif;
254 }
255
256
257
258
259
260
261
262
263
264 public static String convertAttributesToLdif( Entry entry ) throws LdapException
265 {
266 return convertAttributesToLdif( entry, DEFAULT_LINE_LENGTH );
267 }
268
269
270
271
272
273
274
275
276
277 public static Attributes getJndiAttributesFromLdif( String ldif ) throws LdapLdifException
278 {
279 try ( LdifAttributesReader reader = new LdifAttributesReader() )
280 {
281 Attributes attributes = AttributeUtils.toAttributes( reader.parseEntry( ldif ) );
282
283 reader.close();
284
285 return attributes;
286 }
287 catch ( IOException ioe )
288 {
289 throw new LdapLdifException( ioe.getMessage() );
290 }
291 }
292
293
294
295
296
297
298
299
300
301
302 public static String convertToLdif( Entry entry, int length ) throws LdapException
303 {
304 StringBuilder sb = new StringBuilder();
305
306 if ( entry.getDn() != null )
307 {
308
309 if ( isLDIFSafe( entry.getDn().getName() ) )
310 {
311 sb.append( stripLineToNChars( "dn: " + entry.getDn().getName(), length ) );
312 }
313 else
314 {
315 sb.append( stripLineToNChars( "dn:: " + encodeBase64( entry.getDn().getName() ), length ) );
316 }
317
318 sb.append( '\n' );
319 }
320
321
322 for ( Attribute attribute : entry )
323 {
324 sb.append( convertToLdif( attribute, length ) );
325 }
326
327 return sb.toString();
328 }
329
330
331
332
333
334
335
336
337
338
339 public static String convertAttributesToLdif( Entry entry, int length ) throws LdapException
340 {
341 StringBuilder sb = new StringBuilder();
342
343
344 for ( Attribute attribute : entry )
345 {
346 sb.append( convertToLdif( attribute, length ) );
347 }
348
349 return sb.toString();
350 }
351
352
353
354
355
356
357
358
359
360 public static String convertToLdif( LdifEntry entry ) throws LdapException
361 {
362 return convertToLdif( entry, DEFAULT_LINE_LENGTH );
363 }
364
365
366
367
368
369
370
371
372
373
374 public static String convertToLdif( LdifEntry entry, int length ) throws LdapException
375 {
376 StringBuilder sb = new StringBuilder();
377
378
379 if ( isLDIFSafe( entry.getDn().getName() ) )
380 {
381 sb.append( stripLineToNChars( "dn: " + entry.getDn(), length ) );
382 }
383 else
384 {
385 sb.append( stripLineToNChars( "dn:: " + encodeBase64( entry.getDn().getName() ), length ) );
386 }
387
388 sb.append( '\n' );
389
390
391 String changeType = Strings.toLowerCaseAscii( entry.getChangeType().toString() );
392
393 if ( entry.getChangeType() != ChangeType.None )
394 {
395
396 if ( entry.hasControls() )
397 {
398 for ( LdifControl control : entry.getControls().values() )
399 {
400 StringBuilder controlStr = new StringBuilder();
401
402 controlStr.append( "control: " ).append( control.getOid() );
403 controlStr.append( " " ).append( control.isCritical() );
404
405 if ( control.hasValue() )
406 {
407 controlStr.append( "::" ).append( Base64.encode( control.getValue() ) );
408 }
409
410 sb.append( stripLineToNChars( controlStr.toString(), length ) );
411 sb.append( '\n' );
412 }
413 }
414
415 sb.append( stripLineToNChars( "changetype: " + changeType, length ) );
416 sb.append( '\n' );
417 }
418
419 switch ( entry.getChangeType() )
420 {
421 case None:
422 if ( entry.hasControls() )
423 {
424 sb.append( stripLineToNChars( "changetype: " + ChangeType.Add, length ) );
425 }
426
427
428
429 case Add:
430 if ( entry.getEntry() == null )
431 {
432 throw new LdapException( I18n.err( I18n.ERR_12082 ) );
433 }
434
435
436 for ( Attribute attribute : entry.getEntry() )
437 {
438 sb.append( convertToLdif( attribute, length ) );
439 }
440
441 break;
442
443 case Delete:
444 if ( entry.getEntry() != null )
445 {
446 throw new LdapException( I18n.err( I18n.ERR_12081 ) );
447 }
448
449 break;
450
451 case ModDn:
452 case ModRdn:
453 if ( entry.getEntry() != null )
454 {
455 throw new LdapException( I18n.err( I18n.ERR_12083 ) );
456 }
457
458
459 Attribute newRdn = new DefaultAttribute( "newrdn", entry.getNewRdn() );
460 sb.append( convertToLdif( newRdn, length ) );
461
462
463 sb.append( "deleteoldrdn: " );
464
465 if ( entry.isDeleteOldRdn() )
466 {
467 sb.append( "1" );
468 }
469 else
470 {
471 sb.append( "0" );
472 }
473
474 sb.append( '\n' );
475
476
477 if ( !Strings.isEmpty( entry.getNewSuperior() ) )
478 {
479 Attribute newSuperior = new DefaultAttribute( "newsuperior", entry.getNewSuperior() );
480 sb.append( convertToLdif( newSuperior, length ) );
481 }
482
483 break;
484
485 case Modify:
486 boolean isFirst = true;
487
488 for ( Modification modification : entry.getModifications() )
489 {
490
491 if ( isFirst )
492 {
493 isFirst = false;
494 }
495 else
496 {
497 sb.append( "-\n" );
498 }
499
500 switch ( modification.getOperation() )
501 {
502 case ADD_ATTRIBUTE:
503 sb.append( "add: " );
504 break;
505
506 case REMOVE_ATTRIBUTE:
507 sb.append( "delete: " );
508 break;
509
510 case REPLACE_ATTRIBUTE:
511 sb.append( "replace: " );
512 break;
513
514 default:
515 throw new IllegalArgumentException( "Unexpected ModificationOperation: "
516 + modification.getOperation() );
517 }
518
519 sb.append( modification.getAttribute().getUpId() );
520 sb.append( '\n' );
521
522 sb.append( convertToLdif( modification.getAttribute(), length ) );
523 }
524
525 sb.append( '-' );
526 break;
527
528 default:
529 throw new IllegalArgumentException( "Unexpected ChangeType: " + entry.getChangeType() );
530 }
531
532 sb.append( '\n' );
533
534 return sb.toString();
535 }
536
537
538
539
540
541
542
543
544 private static String encodeBase64( String str )
545 {
546
547 return new String( Base64.encode( Strings.getBytesUtf8( str ) ) );
548 }
549
550
551
552
553
554
555
556
557
558 public static String convertToLdif( Attribute attr ) throws LdapException
559 {
560 return convertToLdif( attr, DEFAULT_LINE_LENGTH );
561 }
562
563
564
565
566
567
568
569
570
571
572 public static String convertToLdif( Attribute attr, int length ) throws LdapException
573 {
574 StringBuilder sb = new StringBuilder();
575
576 if ( attr.size() == 0 )
577 {
578
579 return "";
580 }
581
582 for ( Value<?> value : attr )
583 {
584 StringBuilder lineBuffer = new StringBuilder();
585
586 lineBuffer.append( attr.getUpId() );
587
588
589 if ( value.isNull() )
590 {
591 lineBuffer.append( ':' );
592 }
593 else if ( value.isHumanReadable() )
594 {
595
596 String str = value.getString();
597
598 if ( !LdifUtils.isLDIFSafe( str ) )
599 {
600 lineBuffer.append( ":: " ).append( encodeBase64( str ) );
601 }
602 else
603 {
604 lineBuffer.append( ':' );
605
606 if ( str != null )
607 {
608 lineBuffer.append( ' ' ).append( str );
609 }
610 }
611 }
612 else
613 {
614
615 char[] encoded = Base64.encode( value.getBytes() );
616
617 lineBuffer.append( ":: " + new String( encoded ) );
618 }
619
620 lineBuffer.append( '\n' );
621 sb.append( stripLineToNChars( lineBuffer.toString(), length ) );
622 }
623
624 return sb.toString();
625 }
626
627
628
629
630
631
632
633
634
635 public static String stripLineToNChars( String str, int nbChars )
636 {
637 int strLength = str.length();
638
639 if ( strLength <= nbChars )
640 {
641 return str;
642 }
643
644 if ( nbChars < 2 )
645 {
646 throw new IllegalArgumentException( I18n.err( I18n.ERR_12084 ) );
647 }
648
649
650
651 int charsPerLine = nbChars - 1;
652
653 int remaining = ( strLength - nbChars ) % charsPerLine;
654
655 int nbLines = 1 + ( ( strLength - nbChars ) / charsPerLine ) + ( remaining == 0 ? 0 : 1 );
656
657 int nbCharsTotal = strLength + nbLines + nbLines - 2;
658
659 char[] buffer = new char[nbCharsTotal];
660 char[] orig = str.toCharArray();
661
662 int posSrc = 0;
663 int posDst = 0;
664
665 System.arraycopy( orig, posSrc, buffer, posDst, nbChars );
666 posSrc += nbChars;
667 posDst += nbChars;
668
669 for ( int i = 0; i < nbLines - 2; i++ )
670 {
671 buffer[posDst++] = '\n';
672 buffer[posDst++] = ' ';
673
674 System.arraycopy( orig, posSrc, buffer, posDst, charsPerLine );
675 posSrc += charsPerLine;
676 posDst += charsPerLine;
677 }
678
679 buffer[posDst++] = '\n';
680 buffer[posDst++] = ' ';
681 System.arraycopy( orig, posSrc, buffer, posDst, remaining == 0 ? charsPerLine : remaining );
682
683 return new String( buffer );
684 }
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704 public static Attributes createJndiAttributes( Object... avas ) throws LdapException
705 {
706 StringBuilder sb = new StringBuilder();
707 int pos = 0;
708 boolean valueExpected = false;
709
710 for ( Object ava : avas )
711 {
712 if ( !valueExpected )
713 {
714 if ( !( ava instanceof String ) )
715 {
716 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
717 I18n.ERR_12085, pos + 1 ) );
718 }
719
720 String attribute = ( String ) ava;
721 sb.append( attribute );
722
723 if ( attribute.indexOf( ':' ) != -1 )
724 {
725 sb.append( '\n' );
726 }
727 else
728 {
729 valueExpected = true;
730 }
731 }
732 else
733 {
734 if ( ava instanceof String )
735 {
736 sb.append( ": " ).append( ( String ) ava ).append( '\n' );
737 }
738 else if ( ava instanceof byte[] )
739 {
740 sb.append( ":: " );
741 sb.append( new String( Base64.encode( ( byte[] ) ava ) ) );
742 sb.append( '\n' );
743 }
744 else
745 {
746 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
747 I18n.ERR_12086, pos + 1 ) );
748 }
749
750 valueExpected = false;
751 }
752 }
753
754 if ( valueExpected )
755 {
756 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n
757 .err( I18n.ERR_12087 ) );
758 }
759
760 LdifAttributesReader reader = new LdifAttributesReader();
761 Attributes attributes = AttributeUtils.toAttributes( reader.parseEntry( sb.toString() ) );
762
763 try
764 {
765 reader.close();
766 }
767 catch ( IOException e )
768 {
769 e.printStackTrace();
770 }
771
772 return attributes;
773 }
774 }