1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.partition.ldif;
21
22
23 import java.io.File;
24 import java.io.FileFilter;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.UUID;
30
31 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
32 import org.apache.directory.api.ldap.model.csn.CsnFactory;
33 import org.apache.directory.api.ldap.model.cursor.Cursor;
34 import org.apache.directory.api.ldap.model.entry.DefaultEntry;
35 import org.apache.directory.api.ldap.model.entry.Entry;
36 import org.apache.directory.api.ldap.model.entry.Modification;
37 import org.apache.directory.api.ldap.model.exception.LdapException;
38 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
39 import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException;
40 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
41 import org.apache.directory.api.ldap.model.ldif.LdifEntry;
42 import org.apache.directory.api.ldap.model.ldif.LdifReader;
43 import org.apache.directory.api.ldap.model.ldif.LdifUtils;
44 import org.apache.directory.api.ldap.model.name.Ava;
45 import org.apache.directory.api.ldap.model.name.Dn;
46 import org.apache.directory.api.ldap.model.name.Rdn;
47 import org.apache.directory.api.ldap.model.schema.AttributeType;
48 import org.apache.directory.api.ldap.model.schema.SchemaManager;
49 import org.apache.directory.api.util.Strings;
50 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
51 import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
52 import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
53 import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
54 import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
55 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
56 import org.apache.directory.server.i18n.I18n;
57 import org.apache.directory.server.xdbm.IndexEntry;
58 import org.apache.directory.server.xdbm.ParentIdAndRdn;
59 import org.apache.directory.server.xdbm.SingletonIndexCursor;
60 import org.apache.directory.server.xdbm.search.cursor.DescendantCursor;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
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 public class LdifPartition extends AbstractLdifPartition
89 {
90
91 private static Logger LOG = LoggerFactory.getLogger( LdifPartition.class );
92
93
94 private File suffixDirectory;
95
96
97 private static final boolean CREATE = Boolean.TRUE;
98 private static final boolean DELETE = Boolean.FALSE;
99
100
101 private FileFilter dirFilter = new FileFilter()
102 {
103 public boolean accept( File dir )
104 {
105 return dir.isDirectory();
106 }
107 };
108
109
110 private FileFilter entryFilter = new FileFilter()
111 {
112 public boolean accept( File dir )
113 {
114 if ( dir.getName().endsWith( CONF_FILE_EXTN ) )
115 {
116 return dir.isFile();
117 }
118 else
119 {
120 return false;
121 }
122 }
123 };
124
125
126
127
128
129 public LdifPartition( SchemaManager schemaManager )
130 {
131 super( schemaManager );
132 }
133
134
135
136
137
138 protected void doInit() throws Exception
139 {
140 if ( !initialized )
141 {
142 File partitionDir = new File( getPartitionPath() );
143
144
145
146 if ( ( suffixDn == null ) || ( suffixDn.isEmpty() ) )
147 {
148 String msg = I18n.err( I18n.ERR_150 );
149 LOG.error( msg );
150 throw new LdapInvalidDnException( msg );
151 }
152
153 suffixDn.apply( schemaManager );
154
155 String suffixDirName = getFileName( suffixDn );
156 suffixDirectory = new File( partitionDir, suffixDirName );
157
158 super.doInit();
159
160
161
162 if ( suffixDirectory.exists() )
163 {
164 loadEntries( partitionDir );
165 }
166 else
167 {
168
169 try
170 {
171 suffixDirectory.mkdirs();
172 }
173 catch ( SecurityException se )
174 {
175 String msg = I18n.err( I18n.ERR_151, suffixDirectory.getAbsolutePath(), se.getLocalizedMessage() );
176 LOG.error( msg );
177 throw se;
178 }
179
180
181 File contextEntryFile = new File( suffixDirectory + CONF_FILE_EXTN );
182
183 LOG.info( "ldif file doesn't exist {}, creating it.", contextEntryFile.getAbsolutePath() );
184
185 if ( contextEntry == null )
186 {
187 if ( contextEntryFile.exists() )
188 {
189 LdifReader reader = new LdifReader( contextEntryFile );
190 contextEntry = new DefaultEntry( schemaManager, reader.next().getEntry() );
191 reader.close();
192 }
193 else
194 {
195
196
197 return;
198 }
199 }
200
201
202 if ( ( suffixDn != null ) && ( contextEntry != null ) )
203 {
204 Dn contextEntryDn = contextEntry.getDn();
205
206
207 if ( !contextEntryDn.isSchemaAware() )
208 {
209 contextEntryDn.apply( schemaManager );
210 }
211
212
213 if ( suffixDn.equals( contextEntryDn ) )
214 {
215
216 Entry suffixEntry = lookup( new LookupOperationContext( null, suffixDn ) );
217
218
219 if ( suffixEntry == null )
220 {
221
222 if ( !contextEntry.isSchemaAware() )
223 {
224
225 contextEntry = new DefaultEntry( schemaManager, contextEntry );
226 }
227
228
229 if ( contextEntry.get( SchemaConstants.ENTRY_CSN_AT ) == null )
230 {
231 contextEntry.add( SchemaConstants.ENTRY_CSN_AT, new CsnFactory( 0 ).newInstance()
232 .toString() );
233 }
234
235
236 if ( contextEntry.get( SchemaConstants.ENTRY_UUID_AT ) == null )
237 {
238 String uuid = UUID.randomUUID().toString();
239 contextEntry.add( SchemaConstants.ENTRY_UUID_AT, uuid );
240 }
241
242
243 add( new AddOperationContext( null, contextEntry ) );
244 }
245 }
246 }
247 }
248 }
249 }
250
251
252
253
254
255
256
257
258 public void add( AddOperationContext addContext ) throws LdapException
259 {
260 super.add( addContext );
261
262 addEntry( addContext.getEntry() );
263 }
264
265
266
267
268
269 public Entry delete( String id ) throws LdapException
270 {
271 Entry deletedEntry = super.delete( id );
272
273 if ( deletedEntry != null )
274 {
275 File ldifFile = getFile( deletedEntry.getDn(), DELETE );
276
277 boolean deleted = deleteFile( ldifFile );
278
279 LOG.debug( "deleted file {} {}", ldifFile.getAbsoluteFile(), deleted );
280
281
282 File parentFile = ldifFile.getParentFile();
283
284 if ( parentFile.listFiles().length == 0 )
285 {
286 deleteFile( parentFile );
287
288 LOG.debug( "deleted file {} {}", parentFile.getAbsoluteFile(), deleted );
289 }
290 }
291
292 return deletedEntry;
293 }
294
295
296
297
298
299 public void modify( ModifyOperationContext modifyContext ) throws LdapException
300 {
301 String id = getEntryId( modifyContext.getDn() );
302
303 try
304 {
305 super.modify( modifyContext.getDn(), modifyContext.getModItems().toArray( new Modification[]
306 {} ) );
307 }
308 catch ( Exception e )
309 {
310 throw new LdapOperationException( e.getMessage(), e );
311 }
312
313
314 Entry modifiedEntry = fetch( id, modifyContext.getDn() );
315 modifyContext.setAlteredEntry( modifiedEntry );
316
317
318 modifiedEntry.removeAttributes( ENTRY_DN_AT );
319
320
321 Dn dn = modifyContext.getDn();
322
323
324 try
325 {
326 FileWriter fw = new FileWriter( getFile( dn, DELETE ) );
327 fw.write( LdifUtils.convertToLdif( modifiedEntry, true ) );
328 fw.close();
329 }
330 catch ( IOException ioe )
331 {
332 throw new LdapOperationException( ioe.getMessage(), ioe );
333 }
334 }
335
336
337
338
339
340 public void move( MoveOperationContext moveContext ) throws LdapException
341 {
342 Dn oldDn = moveContext.getDn();
343 String id = getEntryId( oldDn );
344
345 super.move( moveContext );
346
347
348 Entry modifiedEntry = fetch( id, moveContext.getNewDn() );
349
350 try
351 {
352 entryMoved( oldDn, modifiedEntry, id );
353 }
354 catch ( Exception e )
355 {
356 throw new LdapOperationErrorException( e.getMessage(), e );
357 }
358 }
359
360
361
362
363
364 public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
365 {
366 Dn oldDn = moveAndRenameContext.getDn();
367 String id = getEntryId( oldDn );
368
369 super.moveAndRename( moveAndRenameContext );
370
371
372 Entry modifiedEntry = fetch( id, moveAndRenameContext.getNewDn() );
373 moveAndRenameContext.setModifiedEntry( modifiedEntry );
374
375 try
376 {
377 entryMoved( oldDn, modifiedEntry, id );
378 }
379 catch ( Exception e )
380 {
381 throw new LdapOperationErrorException( e.getMessage(), e );
382 }
383 }
384
385
386
387
388
389 public void rename( RenameOperationContext renameContext ) throws LdapException
390 {
391 Dn oldDn = renameContext.getDn();
392 String id = getEntryId( oldDn );
393
394
395 super.rename( renameContext );
396
397
398 Dn newDn = oldDn.getParent().add( renameContext.getNewRdn() );
399 Entry modifiedEntry = fetch( id, newDn );
400 renameContext.setModifiedEntry( modifiedEntry );
401
402
403
404 try
405 {
406 entryMoved( oldDn, modifiedEntry, id );
407 }
408 catch ( Exception e )
409 {
410 throw new LdapOperationErrorException( e.getMessage(), e );
411 }
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425 private void entryMoved( Dn oldEntryDn, Entry modifiedEntry, String entryIdOld ) throws Exception
426 {
427
428 addEntry( modifiedEntry );
429
430 String baseId = getEntryId( modifiedEntry.getDn() );
431
432 ParentIdAndRdn parentIdAndRdn = getRdnIndex().reverseLookup( baseId );
433 IndexEntry indexEntry = new IndexEntry();
434
435 indexEntry.setId( baseId );
436 indexEntry.setKey( parentIdAndRdn );
437
438 Cursor<IndexEntry<ParentIdAndRdn, String>> cursor = new SingletonIndexCursor<ParentIdAndRdn>(
439 indexEntry );
440 String parentId = parentIdAndRdn.getParentId();
441
442 Cursor<IndexEntry<String, String>> scopeCursor = new DescendantCursor( this, baseId, parentId, cursor );
443
444
445 try
446 {
447 while ( scopeCursor.next() )
448 {
449 IndexEntry<String, String> entry = scopeCursor.get();
450
451
452 if ( entry.getId() != entryIdOld )
453 {
454 addEntry( fetch( entry.getId() ) );
455 }
456 }
457
458 scopeCursor.close();
459 }
460 catch ( Exception e )
461 {
462 throw new LdapOperationException( e.getMessage(), e );
463 }
464
465
466 File file = getFile( oldEntryDn, DELETE );
467 boolean deleted = deleteFile( file );
468 LOG.warn( "move operation: deleted file {} {}", file.getAbsoluteFile(), deleted );
469
470
471 String dirName = file.getAbsolutePath();
472 dirName = dirName.substring( 0, dirName.indexOf( CONF_FILE_EXTN ) );
473 deleted = deleteFile( new File( dirName ) );
474 LOG.warn( "move operation: deleted dir {} {}", dirName, deleted );
475 }
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494 private void loadEntries( File entryDir ) throws Exception
495 {
496 LOG.debug( "Processing dir {}", entryDir.getName() );
497
498
499 File[] entries = entryDir.listFiles( entryFilter );
500
501 if ( ( entries != null ) && ( entries.length != 0 ) )
502 {
503 LdifReader ldifReader = new LdifReader();
504
505 for ( File entry : entries )
506 {
507 LOG.debug( "parsing ldif file {}", entry.getName() );
508 List<LdifEntry> ldifEntries = ldifReader.parseLdifFile( entry.getAbsolutePath() );
509 ldifReader.close();
510
511 if ( ( ldifEntries != null ) && !ldifEntries.isEmpty() )
512 {
513
514 LdifEntry ldifEntry = ldifEntries.get( 0 );
515 LOG.debug( "Adding entry {}", ldifEntry );
516
517 Entry serverEntry = new DefaultEntry( schemaManager, ldifEntry.getEntry() );
518
519 if ( !serverEntry.containsAttribute( SchemaConstants.ENTRY_CSN_AT ) )
520 {
521 serverEntry.put( SchemaConstants.ENTRY_CSN_AT, defaultCSNFactory.newInstance().toString() );
522 }
523
524 if ( !serverEntry.containsAttribute( SchemaConstants.ENTRY_UUID_AT ) )
525 {
526 serverEntry.put( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
527 }
528
529
530 AddOperationContext addContext = new AddOperationContext( null, serverEntry );
531 super.add( addContext );
532 }
533 }
534
535 }
536 else
537 {
538
539 return;
540 }
541
542
543 File[] dirs = entryDir.listFiles( dirFilter );
544
545 if ( ( dirs != null ) && ( dirs.length != 0 ) )
546 {
547 for ( File f : dirs )
548 {
549 loadEntries( f );
550 }
551 }
552 }
553
554
555
556
557
558 private File getFile( Dn entryDn, boolean create ) throws LdapException
559 {
560 String parentDir = null;
561 String rdnFileName = null;
562
563 if ( entryDn.equals( suffixDn ) )
564 {
565 parentDir = suffixDirectory.getParent() + File.separator;
566 rdnFileName = suffixDn.getName() + CONF_FILE_EXTN;
567 }
568 else
569 {
570 StringBuilder filePath = new StringBuilder();
571 filePath.append( suffixDirectory ).append( File.separator );
572
573 Dn baseDn = entryDn.getDescendantOf( suffixDn );
574 int size = baseDn.size();
575
576 for ( int i = 0; i < size - 1; i++ )
577 {
578 rdnFileName = getFileName( baseDn.getRdn( size - 1 - i ) );
579
580 filePath.append( rdnFileName ).append( File.separator );
581 }
582
583 rdnFileName = getFileName( entryDn.getRdn() ) + CONF_FILE_EXTN;
584 parentDir = filePath.toString();
585 }
586
587 File dir = new File( parentDir );
588
589 if ( !dir.exists() && create )
590 {
591
592 if ( !dir.mkdir() )
593 {
594 throw new LdapException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECORY, dir ) );
595 }
596 }
597
598 File ldifFile = new File( parentDir + rdnFileName );
599
600 if ( ldifFile.exists() && create )
601 {
602
603 throw new LdapException( I18n.err( I18n.ERR_633 ) );
604 }
605
606 return ldifFile;
607 }
608
609
610
611
612
613
614
615
616 private String getFileName( Rdn rdn ) throws LdapException
617 {
618 StringBuilder fileName = new StringBuilder( "" );
619
620 Iterator<Ava> iterator = rdn.iterator();
621
622 while ( iterator.hasNext() )
623 {
624 Ava ava = iterator.next();
625
626
627 String normAT = ava.getNormType();
628 AttributeType at = schemaManager.lookupAttributeTypeRegistry( normAT );
629
630 String atName = at.getName();
631
632
633 String normValue = ava.getNormValue().getString();
634
635 fileName.append( atName ).append( "=" ).append( normValue );
636
637 if ( iterator.hasNext() )
638 {
639 fileName.append( "+" );
640 }
641 }
642
643 return getOSFileName( fileName.toString() );
644 }
645
646
647
648
649
650
651
652
653 private String getFileName( Dn dn ) throws LdapException
654 {
655 StringBuilder sb = new StringBuilder();
656 boolean isFirst = true;
657
658 for ( Rdn rdn : dn.getRdns() )
659 {
660
661 String normAT = rdn.getNormType();
662 AttributeType at = schemaManager.lookupAttributeTypeRegistry( normAT );
663
664 String atName = at.getName();
665
666
667 String normValue = rdn.getNormValue().getString();
668
669 if ( isFirst )
670 {
671 isFirst = false;
672 }
673 else
674 {
675 sb.append( "," );
676 }
677
678 sb.append( atName ).append( "=" ).append( normValue );
679 }
680
681 return getOSFileName( sb.toString() );
682 }
683
684
685
686
687
688
689
690 private String getOSFileName( String fileName )
691 {
692 StringBuilder sb = new StringBuilder();
693
694 for ( char c : fileName.toCharArray() )
695 {
696 switch ( c )
697 {
698 case 0x00:
699 case 0x01:
700 case 0x02:
701 case 0x03:
702 case 0x04:
703 case 0x05:
704 case 0x06:
705 case 0x07:
706 case 0x08:
707 case 0x09:
708 case 0x0A:
709 case 0x0B:
710 case 0x0C:
711 case 0x0D:
712 case 0x0E:
713 case 0x0F:
714 case 0x10:
715 case 0x11:
716 case 0x12:
717 case 0x13:
718 case 0x14:
719 case 0x15:
720 case 0x16:
721 case 0x17:
722 case 0x18:
723 case 0x19:
724 case 0x1A:
725 case 0x1B:
726 case 0x1C:
727 case 0x1D:
728 case 0x1E:
729 case 0x1F:
730 case 0x7F:
731 case ' ':
732 case '"':
733 case '%':
734 case '&':
735 case '(':
736 case ')':
737 case '*':
738 case '+':
739 case '/':
740 case ':':
741 case ';':
742 case '<':
743 case '>':
744 case '?':
745 case '[':
746 case '\\':
747 case ']':
748 case '|':
749 sb.append( "%" ).append( Strings.dumpHex( ( byte ) ( c >> 4 ) ) )
750 .append( Strings.dumpHex( ( byte ) ( c & 0xF ) ) );
751 break;
752
753 default:
754 sb.append( c );
755 break;
756 }
757 }
758
759 return Strings.toLowerCase( sb.toString() );
760 }
761
762
763
764
765
766
767 private void addEntry( Entry entry ) throws LdapException
768 {
769
770 entry.removeAttributes( ENTRY_DN_AT );
771
772 try
773 {
774 FileWriter fw = new FileWriter( getFile( entry.getDn(), CREATE ) );
775 fw.write( LdifUtils.convertToLdif( entry ) );
776 fw.close();
777 }
778 catch ( IOException ioe )
779 {
780 throw new LdapOperationException( ioe.getMessage(), ioe );
781 }
782 }
783
784
785
786
787
788
789
790 private boolean deleteFile( File file )
791 {
792 if ( file.isDirectory() )
793 {
794 File[] files = file.listFiles();
795
796
797 for ( File f : files )
798 {
799 deleteFile( f );
800 }
801
802
803 return file.delete();
804 }
805 else
806 {
807 return file.delete();
808 }
809 }
810 }