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.BufferedReader;
24 import java.io.Closeable;
25 import java.io.DataInputStream;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileNotFoundException;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.Reader;
34 import java.io.StringReader;
35 import java.io.UnsupportedEncodingException;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.nio.charset.Charset;
39 import java.util.ArrayList;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.NoSuchElementException;
43
44 import org.apache.directory.api.asn1.util.Oid;
45 import org.apache.directory.api.i18n.I18n;
46 import org.apache.directory.api.ldap.model.entry.Attribute;
47 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
48 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
49 import org.apache.directory.api.ldap.model.exception.LdapException;
50 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
51 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
52 import org.apache.directory.api.ldap.model.message.Control;
53 import org.apache.directory.api.ldap.model.name.Dn;
54 import org.apache.directory.api.util.Base64;
55 import org.apache.directory.api.util.Chars;
56 import org.apache.directory.api.util.Strings;
57 import org.apache.directory.api.util.exception.NotImplementedException;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 public class LdifReader implements Iterable<LdifEntry>, Closeable
169 {
170
171 private static final Logger LOG = LoggerFactory.getLogger( LdifReader.class );
172
173
174 protected List<String> lines;
175
176
177 protected int position;
178
179
180 protected static final int DEFAULT_VERSION = 1;
181
182
183 protected int version;
184
185
186 protected static final int LDIF_ENTRY = 0;
187
188
189 protected static final int CHANGE = 1;
190
191
192 protected static final int UNKNOWN = 2;
193
194
195 protected long sizeLimit = SIZE_LIMIT_DEFAULT;
196
197
198 protected static final long SIZE_LIMIT_DEFAULT = 1024000;
199
200
201 protected static final int MOD_SPEC = 0;
202
203
204 protected static final int ATTRVAL_SPEC = 1;
205
206
207 protected static final int ATTRVAL_SPEC_OR_SEP = 2;
208
209
210 protected LdifEntry prefetched;
211
212
213 protected Reader reader;
214
215
216 protected boolean containsEntries;
217
218
219 protected boolean containsChanges;
220
221
222
223
224
225 protected Exception error;
226
227
228 protected int entryLen = 0;
229
230
231 protected long entryOffset = 0;
232
233
234 protected long offset = 0;
235
236
237 protected int lineNumber;
238
239
240 protected boolean validateDn = true;
241
242
243
244
245 public LdifReader()
246 {
247 lines = new ArrayList<String>();
248 position = 0;
249 version = DEFAULT_VERSION;
250 }
251
252
253 private void initReader( BufferedReader reader ) throws LdapException
254 {
255 this.reader = reader;
256 init();
257 }
258
259
260 protected void init() throws LdapException
261 {
262 lines = new ArrayList<String>();
263 position = 0;
264 version = DEFAULT_VERSION;
265 containsChanges = false;
266 containsEntries = false;
267
268
269 version = parseVersion();
270 prefetched = parseEntry();
271 }
272
273
274
275
276
277
278
279
280 public LdifReader( String ldifFileName ) throws LdapLdifException
281 {
282 File file = new File( ldifFileName );
283
284 if ( !file.exists() )
285 {
286 String msg = I18n.err( I18n.ERR_12010_CANNOT_FIND_FILE, file.getAbsoluteFile() );
287 LOG.error( msg );
288 throw new LdapLdifException( msg );
289 }
290
291 if ( !file.canRead() )
292 {
293 String msg = I18n.err( I18n.ERR_12011_CANNOT_READ_FILE, file.getName() );
294 LOG.error( msg );
295 throw new LdapLdifException( msg );
296 }
297
298 try
299 {
300 initReader( new BufferedReader( new FileReader( file ) ) );
301 }
302 catch ( FileNotFoundException fnfe )
303 {
304 String msg = I18n.err( I18n.ERR_12010_CANNOT_FIND_FILE, file.getAbsoluteFile() );
305 LOG.error( msg );
306 throw new LdapLdifException( msg, fnfe );
307 }
308 catch ( LdapInvalidDnException lide )
309 {
310 throw new LdapLdifException( lide.getMessage(), lide );
311 }
312 catch ( LdapException le )
313 {
314 throw new LdapLdifException( le.getMessage(), le );
315 }
316 }
317
318
319
320
321
322
323
324
325 public LdifReader( Reader in ) throws LdapException
326 {
327 initReader( new BufferedReader( in ) );
328 }
329
330
331
332
333
334
335
336
337 public LdifReader( InputStream in ) throws LdapException
338 {
339 initReader( new BufferedReader( new InputStreamReader( in ) ) );
340 }
341
342
343
344
345
346
347
348
349 public LdifReader( File file ) throws LdapLdifException
350 {
351 if ( !file.exists() )
352 {
353 String msg = I18n.err( I18n.ERR_12010_CANNOT_FIND_FILE, file.getAbsoluteFile() );
354 LOG.error( msg );
355 throw new LdapLdifException( msg );
356 }
357
358 if ( !file.canRead() )
359 {
360 String msg = I18n.err( I18n.ERR_12011_CANNOT_READ_FILE, file.getName() );
361 LOG.error( msg );
362 throw new LdapLdifException( msg );
363 }
364
365 try
366 {
367 initReader( new BufferedReader( new FileReader( file ) ) );
368 }
369 catch ( FileNotFoundException fnfe )
370 {
371 String msg = I18n.err( I18n.ERR_12010_CANNOT_FIND_FILE, file.getAbsoluteFile() );
372 LOG.error( msg );
373 throw new LdapLdifException( msg, fnfe );
374 }
375 catch ( LdapInvalidDnException lide )
376 {
377 throw new LdapLdifException( lide.getMessage(), lide );
378 }
379 catch ( LdapException le )
380 {
381 throw new LdapLdifException( le.getMessage(), le );
382 }
383 }
384
385
386
387
388
389 public int getVersion()
390 {
391 return version;
392 }
393
394
395
396
397
398 public long getSizeLimit()
399 {
400 return sizeLimit;
401 }
402
403
404
405
406
407
408
409 public void setSizeLimit( long sizeLimit )
410 {
411 this.sizeLimit = sizeLimit;
412 }
413
414
415
416 private void parseFill( char[] document )
417 {
418 while ( Chars.isCharASCII( document, position, ' ' ) )
419 {
420 position++;
421 }
422 }
423
424
425
426
427
428
429
430
431
432
433
434
435
436 private String parseNumber( char[] document )
437 {
438 int initPos = position;
439
440 while ( true )
441 {
442 if ( Chars.isDigit( document, position ) )
443 {
444 position++;
445 }
446 else
447 {
448 break;
449 }
450 }
451
452 if ( position == initPos )
453 {
454 return null;
455 }
456 else
457 {
458 return new String( document, initPos, position - initPos );
459 }
460 }
461
462
463
464
465
466
467
468
469 protected ChangeType parseChangeType( String line )
470 {
471 ChangeType operation = ChangeType.Add;
472
473 String modOp = Strings.trim( line.substring( "changetype:".length() + 1 ) );
474
475 if ( "add".equalsIgnoreCase( modOp ) )
476 {
477 operation = ChangeType.Add;
478 }
479 else if ( "delete".equalsIgnoreCase( modOp ) )
480 {
481 operation = ChangeType.Delete;
482 }
483 else if ( "modify".equalsIgnoreCase( modOp ) )
484 {
485 operation = ChangeType.Modify;
486 }
487 else if ( "moddn".equalsIgnoreCase( modOp ) )
488 {
489 operation = ChangeType.ModDn;
490 }
491 else if ( "modrdn".equalsIgnoreCase( modOp ) )
492 {
493 operation = ChangeType.ModRdn;
494 }
495
496 return operation;
497 }
498
499
500
501
502
503
504
505
506
507 protected String parseDn( String line ) throws LdapLdifException
508 {
509 String dn;
510
511 String lowerLine = Strings.toLowerCase( line );
512
513 if ( lowerLine.startsWith( "dn:" ) || lowerLine.startsWith( "Dn:" ) )
514 {
515
516 int length = line.length();
517
518 if ( length == 3 )
519 {
520
521 dn = "";
522 }
523 else if ( line.charAt( 3 ) == ':' )
524 {
525 if ( length > 4 )
526 {
527
528 String trimmedLine = line.substring( 4 ).trim();
529
530 try
531 {
532 dn = new String( Base64.decode( trimmedLine.toCharArray() ), "UTF-8" );
533 }
534 catch ( UnsupportedEncodingException uee )
535 {
536
537 LOG.error( I18n.err( I18n.ERR_12014_BASE64_DN_EXPECTED ) );
538 throw new LdapLdifException( I18n.err( I18n.ERR_12015_INVALID_BASE64_DN ), uee );
539 }
540 }
541 else
542 {
543
544 LOG.error( I18n.err( I18n.ERR_12012_EMPTY_DN_NOT_ALLOWED ) );
545 throw new LdapLdifException( I18n.err( I18n.ERR_12013_NO_DN ) );
546 }
547 }
548 else
549 {
550 dn = line.substring( 3 ).trim();
551 }
552 }
553 else
554 {
555 LOG.error( I18n.err( I18n.ERR_12016_DN_EXPECTED ) );
556 throw new LdapLdifException( I18n.err( I18n.ERR_12013_NO_DN ) );
557 }
558
559
560 if ( validateDn && !Dn.isValid( dn ) )
561 {
562 String message = I18n.err( I18n.ERR_12017_INVALID_DN, dn );
563 LOG.error( message );
564 throw new LdapLdifException( message );
565 }
566
567 return dn;
568 }
569
570
571
572
573
574
575
576
577
578 protected static Object parseSimpleValue( String line, int pos )
579 {
580 if ( line.length() > pos + 1 )
581 {
582 char c = line.charAt( pos + 1 );
583
584 if ( c == ':' )
585 {
586 String value = Strings.trim( line.substring( pos + 2 ) );
587
588 return Base64.decode( value.toCharArray() );
589 }
590 else
591 {
592 return Strings.trim( line.substring( pos + 1 ) );
593 }
594 }
595 else
596 {
597 return null;
598 }
599 }
600
601
602
603
604
605
606
607
608
609
610 protected Object parseValue( String line, int pos ) throws LdapLdifException
611 {
612 if ( line.length() > pos + 1 )
613 {
614 char c = line.charAt( pos + 1 );
615
616 if ( c == ':' )
617 {
618 String value = Strings.trim( line.substring( pos + 2 ) );
619
620 return Base64.decode( value.toCharArray() );
621 }
622 else if ( c == '<' )
623 {
624 String urlName = Strings.trim( line.substring( pos + 2 ) );
625
626 try
627 {
628 URL url = new URL( urlName );
629
630 if ( "file".equals( url.getProtocol() ) )
631 {
632 String fileName = url.getFile();
633
634 File file = new File( fileName );
635
636 if ( !file.exists() )
637 {
638 LOG.error( I18n.err( I18n.ERR_12018_FILE_NOT_FOUND, fileName ) );
639 throw new LdapLdifException( I18n.err( I18n.ERR_12019_BAD_URL_FILE_NOT_FOUND ) );
640 }
641 else
642 {
643 long length = file.length();
644
645 if ( length > sizeLimit )
646 {
647 String message = I18n.err( I18n.ERR_12020_FILE_TOO_BIG, fileName );
648 LOG.error( message );
649 throw new LdapLdifException( message );
650 }
651 else
652 {
653 byte[] data = new byte[( int ) length];
654 DataInputStream inf = null;
655
656 try
657 {
658 inf = new DataInputStream( new FileInputStream( file ) );
659 inf.readFully( data );
660
661 return data;
662 }
663 catch ( FileNotFoundException fnfe )
664 {
665
666
667
668 LOG.error( I18n.err( I18n.ERR_12018_FILE_NOT_FOUND, fileName ) );
669 throw new LdapLdifException( I18n.err( I18n.ERR_12019_BAD_URL_FILE_NOT_FOUND ),
670 fnfe );
671 }
672 catch ( IOException ioe )
673 {
674 LOG.error( I18n.err( I18n.ERR_12022_ERROR_READING_FILE, fileName ) );
675 throw new LdapLdifException( I18n.err( I18n.ERR_12023_ERROR_READING_BAD_URL ), ioe );
676 }
677 finally
678 {
679 try
680 {
681 if ( inf != null )
682 {
683 inf.close();
684 }
685 }
686 catch ( IOException ioe )
687 {
688 LOG.error( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE, ioe.getMessage() ), ioe );
689
690 }
691 }
692 }
693 }
694 }
695 else
696 {
697 LOG.error( I18n.err( I18n.ERR_12025_BAD_PROTOCOL ) );
698 throw new LdapLdifException( I18n.err( I18n.ERR_12026_UNSUPPORTED_PROTOCOL ) );
699 }
700 }
701 catch ( MalformedURLException mue )
702 {
703 String message = I18n.err( I18n.ERR_12027_BAD_URL, urlName );
704 LOG.error( message );
705 throw new LdapLdifException( message, mue );
706 }
707 }
708 else
709 {
710 String value = Strings.trimLeft( line.substring( pos + 1 ) );
711 int end = value.length();
712
713 for ( int i = value.length() - 1; i > 0; i-- )
714 {
715 char cc = value.charAt( i );
716
717 if ( cc == ' ' )
718 {
719 if ( value.charAt( i - 1 ) == '\\' )
720 {
721
722 break;
723 }
724 else
725 {
726 end = i;
727 }
728 }
729 else
730 {
731 break;
732 }
733 }
734
735 String result = null;
736
737 result = value.substring( 0, end );
738
739 return result;
740 }
741 }
742 else
743 {
744 return null;
745 }
746 }
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771 private Control parseControl( String line ) throws LdapLdifException
772 {
773 String lowerLine = Strings.toLowerCase( line ).trim();
774 char[] controlValue = line.trim().toCharArray();
775 int pos = 0;
776 int length = controlValue.length;
777
778
779 if ( pos > length )
780 {
781
782 LOG.error( I18n.err( I18n.ERR_12029_CONTROL_WITHOUT_OID ) );
783 throw new LdapLdifException( I18n.err( I18n.ERR_12029_CONTROL_WITHOUT_OID ) );
784 }
785
786 int initPos = pos;
787
788 while ( Chars.isCharASCII( controlValue, pos, '.' ) || Chars.isDigit( controlValue, pos ) )
789 {
790 pos++;
791 }
792
793 if ( pos == initPos )
794 {
795
796 LOG.error( I18n.err( I18n.ERR_12029_CONTROL_WITHOUT_OID ) );
797 throw new LdapLdifException( I18n.err( I18n.ERR_12029_CONTROL_WITHOUT_OID ) );
798 }
799
800
801 String oidString = lowerLine.substring( 0, pos );
802
803 if ( !Oid.isOid( oidString ) )
804 {
805 String message = I18n.err( I18n.ERR_12031_INVALID_OID, oidString );
806 LOG.error( message );
807 throw new LdapLdifException( message );
808 }
809
810 LdifControl control = new LdifControl( oidString );
811
812
813
814 while ( Chars.isCharASCII( controlValue, pos, ' ' ) )
815 {
816 pos++;
817 }
818
819
820 int criticalPos = lowerLine.indexOf( ':' );
821
822 int criticalLength;
823
824 if ( criticalPos == -1 )
825 {
826 criticalLength = length - pos;
827 }
828 else
829 {
830 criticalLength = criticalPos - pos;
831 }
832
833 if ( ( criticalLength == 4 ) && ( "true".equalsIgnoreCase( lowerLine.substring( pos, pos + 4 ) ) ) )
834 {
835 control.setCritical( true );
836 }
837 else if ( ( criticalLength == 5 ) && ( "false".equalsIgnoreCase( lowerLine.substring( pos, pos + 5 ) ) ) )
838 {
839 control.setCritical( false );
840 }
841 else if ( criticalLength != 0 )
842 {
843
844
845 LOG.error( I18n.err( I18n.ERR_12033_INVALID_CRITICALITY ) );
846 throw new LdapLdifException( I18n.err( I18n.ERR_12033_INVALID_CRITICALITY ) );
847 }
848
849 if ( criticalPos > 0 )
850 {
851
852
853 if ( Chars.isCharASCII( controlValue, criticalPos + 1, ':' ) )
854 {
855
856
857
858 pos = criticalPos + 2;
859
860 while ( Chars.isCharASCII( controlValue, pos, ' ' ) )
861 {
862 pos++;
863 }
864
865 byte[] value = Base64.decode( line.substring( pos ).toCharArray() );
866 control.setValue( value );
867 }
868 else if ( Chars.isCharASCII( controlValue, criticalPos + 1, '<' ) )
869 {
870
871 throw new NotImplementedException( "See DIRSERVER-1547" );
872 }
873 else
874 {
875
876 pos = criticalPos + 1;
877
878 while ( Chars.isCharASCII( controlValue, pos, ' ' ) )
879 {
880 pos++;
881 }
882
883
884 byte[] value = new byte[length - pos];
885
886 for ( int i = 0; i < length - pos; i++ )
887 {
888 value[i] = ( byte ) controlValue[i + pos];
889 }
890
891 control.setValue( value );
892 }
893 }
894
895 return control;
896 }
897
898
899
900
901
902
903
904
905 public static Attribute parseAttributeValue( String line )
906 {
907 int colonIndex = line.indexOf( ':' );
908
909 if ( colonIndex != -1 )
910 {
911 String attributeType = Strings.toLowerCase( line ).substring( 0, colonIndex );
912 Object attributeValue = parseSimpleValue( line, colonIndex );
913
914
915 if ( attributeValue instanceof String )
916 {
917 return new DefaultAttribute( attributeType, ( String ) attributeValue );
918 }
919 else
920 {
921 return new DefaultAttribute( attributeType, ( byte[] ) attributeValue );
922 }
923 }
924 else
925 {
926 return null;
927 }
928 }
929
930
931
932
933
934
935
936
937
938
939 public void parseAttributeValue( LdifEntry entry, String line, String lowerLine ) throws LdapException
940 {
941 int colonIndex = line.indexOf( ':' );
942
943 String attributeType = lowerLine.substring( 0, colonIndex );
944
945
946 if ( attributeType.equals( "dn" ) )
947 {
948 LOG.error( I18n.err( I18n.ERR_12002_ENTRY_WITH_TWO_DNS ) );
949 throw new LdapLdifException( I18n.err( I18n.ERR_12003_LDIF_ENTRY_WITH_TWO_DNS ) );
950 }
951
952 Object attributeValue = parseValue( line, colonIndex );
953
954
955 entry.addAttribute( attributeType, attributeValue );
956 }
957
958
959
960
961
962
963
964
965
966 private void parseModRdn( LdifEntry entry, Iterator<String> iter ) throws LdapLdifException
967 {
968
969
970 if ( iter.hasNext() )
971 {
972 String line = iter.next();
973 String lowerLine = Strings.toLowerCase( line );
974
975 if ( lowerLine.startsWith( "newrdn::" ) || lowerLine.startsWith( "newrdn:" ) )
976 {
977 int colonIndex = line.indexOf( ':' );
978 Object attributeValue = parseValue( line, colonIndex );
979
980 if ( attributeValue instanceof String )
981 {
982 entry.setNewRdn( ( String ) attributeValue );
983 }
984 else
985 {
986 entry.setNewRdn( Strings.utf8ToString( ( byte[] ) attributeValue ) );
987 }
988 }
989 else
990 {
991 LOG.error( I18n.err( I18n.ERR_12035_BAD_MODRDN_OPERATION ) );
992 throw new LdapLdifException( I18n.err( I18n.ERR_12035_BAD_MODRDN_OPERATION ) );
993 }
994 }
995 else
996 {
997 LOG.error( I18n.err( I18n.ERR_12035_BAD_MODRDN_OPERATION ) );
998 throw new LdapLdifException( I18n.err( I18n.ERR_12035_BAD_MODRDN_OPERATION ) );
999 }
1000
1001 if ( iter.hasNext() )
1002 {
1003 String line = iter.next();
1004 String lowerLine = Strings.toLowerCase( line );
1005
1006 if ( lowerLine.startsWith( "deleteoldrdn:" ) )
1007 {
1008 int colonIndex = line.indexOf( ':' );
1009 Object attributeValue = parseValue( line, colonIndex );
1010 entry.setDeleteOldRdn( "1".equals( attributeValue ) );
1011 }
1012 else
1013 {
1014 LOG.error( I18n.err( I18n.ERR_12038_NO_DELETEOLDRDN ) );
1015 throw new LdapLdifException( I18n.err( I18n.ERR_12038_NO_DELETEOLDRDN ) );
1016 }
1017 }
1018 else
1019 {
1020 LOG.error( I18n.err( I18n.ERR_12038_NO_DELETEOLDRDN ) );
1021 throw new LdapLdifException( I18n.err( I18n.ERR_12038_NO_DELETEOLDRDN ) );
1022 }
1023 }
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 private void parseModify( LdifEntry entry, Iterator<String> iter ) throws LdapLdifException
1045 {
1046 int state = MOD_SPEC;
1047 String modified = null;
1048 ModificationOperation modificationType = ModificationOperation.ADD_ATTRIBUTE;
1049 Attribute attribute = null;
1050
1051
1052 boolean isEmptyValue = true;
1053
1054 while ( iter.hasNext() )
1055 {
1056 String line = iter.next();
1057 String lowerLine = Strings.toLowerCase( line );
1058
1059 if ( lowerLine.startsWith( "-" ) )
1060 {
1061 if ( ( state != ATTRVAL_SPEC_OR_SEP ) && ( state != ATTRVAL_SPEC ) )
1062 {
1063 LOG.error( I18n.err( I18n.ERR_12040_BAD_MODIFY_SEPARATOR ) );
1064 throw new LdapLdifException( I18n.err( I18n.ERR_12040_BAD_MODIFY_SEPARATOR ) );
1065 }
1066 else
1067 {
1068 if ( isEmptyValue )
1069 {
1070
1071 entry.addModification( modificationType, modified, null );
1072 }
1073 else
1074 {
1075
1076 entry.addModification( modificationType, attribute );
1077 }
1078
1079 state = MOD_SPEC;
1080 isEmptyValue = true;
1081 }
1082 }
1083 else if ( lowerLine.startsWith( "add:" ) )
1084 {
1085 if ( ( state != MOD_SPEC ) && ( state != ATTRVAL_SPEC ) )
1086 {
1087 LOG.error( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1088 throw new LdapLdifException( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1089 }
1090
1091 modified = Strings.trim( line.substring( "add:".length() ) );
1092 modificationType = ModificationOperation.ADD_ATTRIBUTE;
1093 attribute = new DefaultAttribute( modified );
1094
1095 state = ATTRVAL_SPEC;
1096 }
1097 else if ( lowerLine.startsWith( "delete:" ) )
1098 {
1099 if ( ( state != MOD_SPEC ) && ( state != ATTRVAL_SPEC ) )
1100 {
1101 LOG.error( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1102 throw new LdapLdifException( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1103 }
1104
1105 modified = Strings.trim( line.substring( "delete:".length() ) );
1106 modificationType = ModificationOperation.REMOVE_ATTRIBUTE;
1107 attribute = new DefaultAttribute( modified );
1108 isEmptyValue = false;
1109
1110 state = ATTRVAL_SPEC_OR_SEP;
1111 }
1112 else if ( lowerLine.startsWith( "replace:" ) )
1113 {
1114 if ( ( state != MOD_SPEC ) && ( state != ATTRVAL_SPEC ) )
1115 {
1116 LOG.error( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1117 throw new LdapLdifException( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1118 }
1119
1120 modified = Strings.trim( line.substring( "replace:".length() ) );
1121 modificationType = ModificationOperation.REPLACE_ATTRIBUTE;
1122 attribute = new DefaultAttribute( modified );
1123
1124 state = ATTRVAL_SPEC_OR_SEP;
1125 }
1126 else
1127 {
1128 if ( ( state != ATTRVAL_SPEC ) && ( state != ATTRVAL_SPEC_OR_SEP ) )
1129 {
1130 LOG.error( I18n.err( I18n.ERR_12040_BAD_MODIFY_SEPARATOR ) );
1131 throw new LdapLdifException( I18n.err( I18n.ERR_12040_BAD_MODIFY_SEPARATOR ) );
1132 }
1133
1134
1135 int colonIndex = line.indexOf( ':' );
1136
1137 String attributeType = line.substring( 0, colonIndex );
1138
1139 if ( !attributeType.equalsIgnoreCase( modified ) )
1140 {
1141 LOG.error( I18n.err( I18n.ERR_12044 ) );
1142 throw new LdapLdifException( I18n.err( I18n.ERR_12045 ) );
1143 }
1144
1145
1146 if ( attributeType.equalsIgnoreCase( "dn" ) )
1147 {
1148 LOG.error( I18n.err( I18n.ERR_12002_ENTRY_WITH_TWO_DNS ) );
1149 throw new LdapLdifException( I18n.err( I18n.ERR_12003_LDIF_ENTRY_WITH_TWO_DNS ) );
1150 }
1151
1152 Object attributeValue = parseValue( line, colonIndex );
1153
1154 try
1155 {
1156 if ( attributeValue instanceof String )
1157 {
1158 attribute.add( ( String ) attributeValue );
1159 }
1160 else
1161 {
1162 attribute.add( ( byte[] ) attributeValue );
1163 }
1164 }
1165 catch ( LdapInvalidAttributeValueException liave )
1166 {
1167 throw new LdapLdifException( liave.getMessage(), liave );
1168 }
1169
1170 isEmptyValue = false;
1171
1172 state = ATTRVAL_SPEC_OR_SEP;
1173 }
1174 }
1175
1176 if ( state != MOD_SPEC )
1177 {
1178 LOG.error( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1179 throw new LdapLdifException( I18n.err( I18n.ERR_12042_BAD_MODIFY_SEPARATOR_2 ) );
1180 }
1181 }
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217 private void parseChange( LdifEntry entry, Iterator<String> iter, ChangeType operation ) throws LdapException
1218 {
1219
1220 entry.setChangeType( operation );
1221
1222 switch ( operation )
1223 {
1224 case Delete:
1225
1226
1227 return;
1228
1229 case Add:
1230
1231 while ( iter.hasNext() )
1232 {
1233 String line = iter.next();
1234 String lowerLine = Strings.toLowerCase( line );
1235 parseAttributeValue( entry, line, lowerLine );
1236 }
1237
1238 return;
1239
1240 case Modify:
1241 parseModify( entry, iter );
1242 return;
1243
1244 case ModDn:
1245 case ModRdn:
1246
1247 parseModRdn( entry, iter );
1248
1249
1250 if ( iter.hasNext() )
1251 {
1252 String line = iter.next();
1253 String lowerLine = Strings.toLowerCase( line );
1254
1255 if ( lowerLine.startsWith( "newsuperior:" ) )
1256 {
1257 int colonIndex = line.indexOf( ':' );
1258 Object attributeValue = parseValue( line, colonIndex );
1259
1260 if ( attributeValue instanceof String )
1261 {
1262 entry.setNewSuperior( ( String ) attributeValue );
1263 }
1264 else
1265 {
1266 entry.setNewSuperior( Strings.utf8ToString( ( byte[] ) attributeValue ) );
1267 }
1268 }
1269 else
1270 {
1271 if ( operation == ChangeType.ModDn )
1272 {
1273 LOG.error( I18n.err( I18n.ERR_12046 ) );
1274 throw new LdapLdifException( I18n.err( I18n.ERR_12047 ) );
1275 }
1276 }
1277 }
1278
1279 return;
1280
1281 default:
1282
1283 LOG.error( I18n.err( I18n.ERR_12048 ) );
1284 throw new LdapLdifException( I18n.err( I18n.ERR_12049 ) );
1285 }
1286 }
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303 protected LdifEntry parseEntry() throws LdapException
1304 {
1305 if ( ( lines == null ) || ( lines.size() == 0 ) )
1306 {
1307 LOG.debug( "The entry is empty : end of ldif file" );
1308 return null;
1309 }
1310
1311
1312 String line = lines.get( 0 );
1313
1314 lineNumber -= ( lines.size() - 1 );
1315
1316 String name = parseDn( line );
1317
1318 Dn dn = new Dn( name );
1319
1320
1321 LdifEntry entry = createLdifEntry();
1322 entry.setLengthBeforeParsing( entryLen );
1323 entry.setOffset( entryOffset );
1324
1325 entry.setDn( dn );
1326
1327
1328 lines.remove( 0 );
1329
1330
1331 Iterator<String> iter = lines.iterator();
1332
1333
1334 int type = LDIF_ENTRY;
1335
1336
1337
1338 boolean controlSeen = false;
1339
1340
1341
1342 boolean changeTypeSeen = false;
1343
1344 ChangeType operation = ChangeType.Add;
1345 String lowerLine;
1346 Control control;
1347
1348 while ( iter.hasNext() )
1349 {
1350 lineNumber++;
1351
1352
1353
1354 line = iter.next();
1355 lowerLine = Strings.toLowerCase( line );
1356
1357
1358
1359
1360
1361 if ( lowerLine.startsWith( "control:" ) )
1362 {
1363 if ( containsEntries )
1364 {
1365 LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
1366 throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
1367 }
1368
1369 containsChanges = true;
1370
1371 if ( controlSeen )
1372 {
1373 LOG.error( I18n.err( I18n.ERR_12050 ) );
1374 throw new LdapLdifException( I18n.err( I18n.ERR_12051 ) );
1375 }
1376
1377
1378 control = parseControl( line.substring( "control:".length() ) );
1379 entry.addControl( control );
1380 }
1381 else if ( lowerLine.startsWith( "changetype:" ) )
1382 {
1383 if ( containsEntries )
1384 {
1385 LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
1386 throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
1387 }
1388
1389 containsChanges = true;
1390
1391 if ( changeTypeSeen )
1392 {
1393 LOG.error( I18n.err( I18n.ERR_12052 ) );
1394 throw new LdapLdifException( I18n.err( I18n.ERR_12053 ) );
1395 }
1396
1397
1398 type = CHANGE;
1399 controlSeen = true;
1400
1401 operation = parseChangeType( line );
1402
1403
1404 parseChange( entry, iter, operation );
1405 changeTypeSeen = true;
1406 }
1407 else if ( line.indexOf( ':' ) > 0 )
1408 {
1409 if ( containsChanges )
1410 {
1411 LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
1412 throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
1413 }
1414
1415 containsEntries = true;
1416
1417 if ( controlSeen || changeTypeSeen )
1418 {
1419 LOG.error( I18n.err( I18n.ERR_12054 ) );
1420 throw new LdapLdifException( I18n.err( I18n.ERR_12055 ) );
1421 }
1422
1423 parseAttributeValue( entry, line, lowerLine );
1424 type = LDIF_ENTRY;
1425 }
1426 else
1427 {
1428
1429 LOG.error( I18n.err( I18n.ERR_12056 ) );
1430 throw new LdapLdifException( I18n.err( I18n.ERR_12057_BAD_ATTRIBUTE ) );
1431 }
1432 }
1433
1434 if ( type == LDIF_ENTRY )
1435 {
1436 LOG.debug( "Read an entry : {}", entry );
1437 }
1438 else if ( type == CHANGE )
1439 {
1440 entry.setChangeType( operation );
1441 LOG.debug( "Read a modification : {}", entry );
1442 }
1443 else
1444 {
1445 LOG.error( I18n.err( I18n.ERR_12058_UNKNOWN_ENTRY_TYPE ) );
1446 throw new LdapLdifException( I18n.err( I18n.ERR_12059_UNKNOWN_ENTRY ) );
1447 }
1448
1449 return entry;
1450 }
1451
1452
1453
1454
1455
1456
1457
1458
1459 protected int parseVersion() throws LdapLdifException
1460 {
1461 int ver = DEFAULT_VERSION;
1462
1463
1464 readLines();
1465
1466 if ( lines.size() == 0 )
1467 {
1468 LOG.warn( "The ldif file is empty" );
1469 return ver;
1470 }
1471
1472
1473 String line = lines.get( 0 );
1474
1475
1476 char[] document = line.toCharArray();
1477 String versionNumber;
1478
1479 if ( line.startsWith( "version:" ) )
1480 {
1481 position += "version:".length();
1482 parseFill( document );
1483
1484
1485 versionNumber = parseNumber( document );
1486
1487
1488 if ( position != document.length )
1489 {
1490 LOG.error( I18n.err( I18n.ERR_12060_VERSION_NOT_A_NUMBER ) );
1491 throw new LdapLdifException( I18n.err( I18n.ERR_12061_LDIF_PARSING_ERROR ) );
1492 }
1493
1494 try
1495 {
1496 ver = Integer.parseInt( versionNumber );
1497 }
1498 catch ( NumberFormatException nfe )
1499 {
1500 LOG.error( I18n.err( I18n.ERR_12060_VERSION_NOT_A_NUMBER ) );
1501 throw new LdapLdifException( I18n.err( I18n.ERR_12061_LDIF_PARSING_ERROR ), nfe );
1502 }
1503
1504 LOG.debug( "Ldif version : {}", versionNumber );
1505
1506
1507 lines.remove( 0 );
1508
1509
1510 if ( lines.size() == 0 )
1511 {
1512
1513 int tmpEntryLen = entryLen;
1514
1515 readLines();
1516
1517 entryLen += tmpEntryLen;
1518 }
1519 }
1520 else
1521 {
1522 LOG.info( "No version information : assuming version: 1" );
1523 }
1524
1525 return ver;
1526 }
1527
1528
1529
1530
1531
1532
1533
1534
1535 protected String getLine() throws IOException
1536 {
1537 return ( ( BufferedReader ) reader ).readLine();
1538 }
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549 protected void readLines() throws LdapLdifException
1550 {
1551 String line;
1552 boolean insideComment = true;
1553 boolean isFirstLine = true;
1554
1555 lines.clear();
1556 entryLen = 0;
1557 entryOffset = offset;
1558
1559 StringBuffer sb = new StringBuffer();
1560
1561 try
1562 {
1563 while ( ( line = getLine() ) != null )
1564 {
1565 lineNumber++;
1566
1567 if ( line.length() == 0 )
1568 {
1569 if ( isFirstLine )
1570 {
1571 continue;
1572 }
1573 else
1574 {
1575
1576 insideComment = false;
1577 offset++;
1578 break;
1579 }
1580 }
1581
1582
1583 switch ( line.charAt( 0 ) )
1584 {
1585 case '#':
1586 insideComment = true;
1587 break;
1588
1589 case ' ':
1590 isFirstLine = false;
1591
1592 if ( insideComment )
1593 {
1594 continue;
1595 }
1596 else if ( sb.length() == 0 )
1597 {
1598 LOG.error( I18n.err( I18n.ERR_12062_EMPTY_CONTINUATION_LINE ) );
1599 throw new LdapLdifException( I18n.err( I18n.ERR_12061_LDIF_PARSING_ERROR ) );
1600 }
1601 else
1602 {
1603 sb.append( line.substring( 1 ) );
1604 }
1605
1606 insideComment = false;
1607 break;
1608
1609 default:
1610 isFirstLine = false;
1611
1612
1613
1614 if ( sb.length() != 0 )
1615 {
1616 lines.add( sb.toString() );
1617 }
1618
1619 sb = new StringBuffer( line );
1620 insideComment = false;
1621 break;
1622 }
1623
1624 byte[] data = line.getBytes();
1625
1626 offset += ( data.length + 1 );
1627 entryLen += ( data.length + 1 );
1628 }
1629 }
1630 catch ( IOException ioe )
1631 {
1632 throw new LdapLdifException( I18n.err( I18n.ERR_12063_ERROR_WHILE_READING_LDIF_LINE ), ioe );
1633 }
1634
1635
1636 if ( sb.length() != 0 )
1637 {
1638 lines.add( sb.toString() );
1639 }
1640 }
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650 public List<LdifEntry> parseLdifFile( String fileName ) throws LdapLdifException
1651 {
1652 return parseLdifFile( fileName, Charset.forName( Strings.getDefaultCharsetName() ).toString() );
1653 }
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664 public List<LdifEntry> parseLdifFile( String fileName, String encoding ) throws LdapLdifException
1665 {
1666 if ( Strings.isEmpty( fileName ) )
1667 {
1668 LOG.error( I18n.err( I18n.ERR_12064_EMPTY_FILE_NAME ) );
1669 throw new LdapLdifException( I18n.err( I18n.ERR_12064_EMPTY_FILE_NAME ) );
1670 }
1671
1672 File file = new File( fileName );
1673
1674 if ( !file.exists() )
1675 {
1676 LOG.error( I18n.err( I18n.ERR_12066, fileName ) );
1677 throw new LdapLdifException( I18n.err( I18n.ERR_12067, fileName ) );
1678 }
1679
1680 BufferedReader reader = null;
1681
1682
1683 try
1684 {
1685 reader = new BufferedReader(
1686 new InputStreamReader( new FileInputStream( file ), Charset.forName( encoding ) ) );
1687
1688 return parseLdif( reader );
1689 }
1690 catch ( FileNotFoundException fnfe )
1691 {
1692 LOG.error( I18n.err( I18n.ERR_12068, fileName ) );
1693 throw new LdapLdifException( I18n.err( I18n.ERR_12067, fileName ), fnfe );
1694 }
1695 catch ( LdapException le )
1696 {
1697 throw new LdapLdifException( le.getMessage(), le );
1698 }
1699 finally
1700 {
1701
1702 try
1703 {
1704 if ( reader != null )
1705 {
1706 reader.close();
1707 }
1708 }
1709 catch ( IOException ioe )
1710 {
1711
1712 }
1713 }
1714 }
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724 public List<LdifEntry> parseLdif( String ldif ) throws LdapLdifException
1725 {
1726 LOG.debug( "Starts parsing ldif buffer" );
1727
1728 if ( Strings.isEmpty( ldif ) )
1729 {
1730 return new ArrayList<LdifEntry>();
1731 }
1732
1733 BufferedReader reader = new BufferedReader( new StringReader( ldif ) );
1734
1735 try
1736 {
1737 List<LdifEntry> entries = parseLdif( reader );
1738
1739 if ( LOG.isDebugEnabled() )
1740 {
1741 LOG.debug( "Parsed {} entries.", ( entries == null ? Integer.valueOf( 0 ) : Integer.valueOf( entries
1742 .size() ) ) );
1743 }
1744
1745 return entries;
1746 }
1747 catch ( LdapLdifException ne )
1748 {
1749 LOG.error( I18n.err( I18n.ERR_12069, ne.getLocalizedMessage() ) );
1750 throw new LdapLdifException( I18n.err( I18n.ERR_12070 ), ne );
1751 }
1752 catch ( LdapException le )
1753 {
1754 throw new LdapLdifException( le.getMessage(), le );
1755 }
1756 finally
1757 {
1758
1759 try
1760 {
1761 reader.close();
1762 }
1763 catch ( IOException ioe )
1764 {
1765 throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
1766 }
1767
1768 }
1769 }
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780 private LdifEntry nextInternal()
1781 {
1782 try
1783 {
1784 LOG.debug( "next(): -- called" );
1785
1786 LdifEntry entry = prefetched;
1787 readLines();
1788
1789 try
1790 {
1791 prefetched = parseEntry();
1792 }
1793 catch ( LdapLdifException ne )
1794 {
1795 error = ne;
1796 throw new NoSuchElementException( ne.getMessage() );
1797 }
1798 catch ( LdapException le )
1799 {
1800 throw new NoSuchElementException( le.getMessage() );
1801 }
1802
1803 LOG.debug( "next(): -- returning ldif {}\n", entry );
1804
1805 return entry;
1806 }
1807 catch ( LdapLdifException ne )
1808 {
1809 LOG.error( I18n.err( I18n.ERR_12071 ) );
1810 error = ne;
1811 return null;
1812 }
1813 }
1814
1815
1816
1817
1818
1819
1820
1821 public LdifEntry next()
1822 {
1823 return nextInternal();
1824 }
1825
1826
1827
1828
1829
1830
1831
1832 private boolean hasNextInternal()
1833 {
1834 return null != prefetched;
1835 }
1836
1837
1838
1839
1840
1841
1842
1843 public boolean hasNext()
1844 {
1845 if ( prefetched != null )
1846 {
1847 LOG.debug( "hasNext(): -- returning true" );
1848 }
1849 else
1850 {
1851 LOG.debug( "hasNext(): -- returning false" );
1852 }
1853
1854 return hasNextInternal();
1855 }
1856
1857
1858
1859
1860
1861
1862
1863 private void removeInternal()
1864 {
1865 throw new UnsupportedOperationException();
1866 }
1867
1868
1869
1870
1871
1872
1873
1874 public void remove()
1875 {
1876 removeInternal();
1877 }
1878
1879
1880
1881
1882
1883 public Iterator<LdifEntry> iterator()
1884 {
1885 return new Iterator<LdifEntry>()
1886 {
1887 public boolean hasNext()
1888 {
1889 return hasNextInternal();
1890 }
1891
1892
1893 public LdifEntry next()
1894 {
1895 return nextInternal();
1896 }
1897
1898
1899 public void remove()
1900 {
1901 throw new UnsupportedOperationException();
1902 }
1903 };
1904 }
1905
1906
1907
1908
1909
1910 public boolean hasError()
1911 {
1912 return error != null;
1913 }
1914
1915
1916
1917
1918
1919 public Exception getError()
1920 {
1921 return error;
1922 }
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933 public List<LdifEntry> parseLdif( BufferedReader reader ) throws LdapException
1934 {
1935
1936 List<LdifEntry> entries = new ArrayList<LdifEntry>();
1937
1938 this.reader = reader;
1939
1940
1941 version = parseVersion();
1942 prefetched = parseEntry();
1943
1944
1945 try
1946 {
1947 for ( LdifEntry entry : this )
1948 {
1949 if ( entry != null )
1950 {
1951 entries.add( entry );
1952 }
1953 }
1954 }
1955 catch ( NoSuchElementException nsee )
1956 {
1957 throw new LdapLdifException( I18n.err( I18n.ERR_12072, error.getLocalizedMessage() ), nsee );
1958 }
1959
1960 return entries;
1961 }
1962
1963
1964
1965
1966
1967 public boolean containsEntries()
1968 {
1969 return containsEntries;
1970 }
1971
1972
1973
1974
1975
1976 public int getLineNumber()
1977 {
1978 return lineNumber;
1979 }
1980
1981
1982
1983
1984
1985
1986 protected LdifEntry createLdifEntry()
1987 {
1988 return new LdifEntry();
1989 }
1990
1991
1992
1993
1994 public boolean isValidateDn()
1995 {
1996 return validateDn;
1997 }
1998
1999
2000
2001
2002
2003
2004
2005 public void setValidateDn( boolean validateDn )
2006 {
2007 this.validateDn = validateDn;
2008 }
2009
2010
2011
2012
2013
2014 public void close() throws IOException
2015 {
2016 if ( reader != null )
2017 {
2018 position = 0;
2019 reader.close();
2020 containsEntries = false;
2021 containsChanges = false;
2022 offset = entryOffset = lineNumber = 0;
2023 }
2024 }
2025 }