1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.common.file.filesystembased;
21
22 import org.waarp.common.command.exception.CommandAbstractException;
23 import org.waarp.common.command.exception.Reply550Exception;
24 import org.waarp.common.command.exception.Reply553Exception;
25 import org.waarp.common.digest.FilesystemBasedDigest;
26 import org.waarp.common.digest.FilesystemBasedDigest.DigestAlgo;
27 import org.waarp.common.file.AbstractDir;
28 import org.waarp.common.file.FileInterface;
29 import org.waarp.common.file.FileUtils;
30 import org.waarp.common.file.OptsMLSxInterface;
31 import org.waarp.common.file.SessionInterface;
32 import org.waarp.common.file.filesystembased.specific.FilesystemBasedCommonsIo;
33 import org.waarp.common.file.filesystembased.specific.FilesystemBasedDirJdk5;
34 import org.waarp.common.file.filesystembased.specific.FilesystemBasedDirJdk6;
35 import org.waarp.common.file.filesystembased.specific.FilesystemBasedDirJdkAbstract;
36 import org.waarp.common.logging.WaarpLogger;
37 import org.waarp.common.logging.WaarpLoggerFactory;
38 import org.waarp.common.utility.DetectionUtils;
39 import org.waarp.common.utility.ParametersChecker;
40
41 import java.io.File;
42 import java.io.FileFilter;
43 import java.io.FileInputStream;
44 import java.io.FileNotFoundException;
45 import java.io.IOException;
46 import java.text.DateFormat;
47 import java.text.SimpleDateFormat;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.Calendar;
51 import java.util.Date;
52 import java.util.List;
53 import java.util.Locale;
54 import java.util.zip.CRC32;
55 import java.util.zip.CheckedInputStream;
56
57 import static org.waarp.common.file.FileUtils.*;
58
59
60
61
62 public abstract class FilesystemBasedDirImpl extends AbstractDir {
63 private static final String ERROR_WHILE_READING_FILE =
64 "Error while reading file: ";
65
66 private static final String DIRECTORY_NOT_FOUND = "Directory not found: ";
67
68
69
70
71 private static final WaarpLogger logger =
72 WaarpLoggerFactory.getLogger(FilesystemBasedDirImpl.class);
73
74
75
76
77 protected static FilesystemBasedDirJdkAbstract filesystemBasedFtpDirJdk;
78
79
80
81
82 static {
83 initJdkDependent();
84 }
85
86
87
88
89 private static void initJdkDependent() {
90 if (DetectionUtils.javaVersion() >= 6) {
91 filesystemBasedFtpDirJdk = new FilesystemBasedDirJdk6();
92 } else {
93 filesystemBasedFtpDirJdk = new FilesystemBasedDirJdk5();
94 }
95 }
96
97
98
99
100
101
102
103
104 @Deprecated
105 public static void initJdkDependent(
106 final FilesystemBasedDirJdkAbstract filesystemBasedFtpDirJdkChoice) {
107 filesystemBasedFtpDirJdk = filesystemBasedFtpDirJdkChoice;
108 }
109
110
111
112
113
114 protected FilesystemBasedDirImpl(final SessionInterface session,
115 final OptsMLSxInterface optsMLSx) {
116 this.session = session;
117 this.optsMLSx = optsMLSx;
118 this.optsMLSx.setOptsModify((byte) 1);
119 this.optsMLSx.setOptsPerm((byte) 1);
120 this.optsMLSx.setOptsSize((byte) 1);
121 this.optsMLSx.setOptsType((byte) 1);
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 @Override
142 protected final List<String> wildcardFiles(final String pathWithWildcard)
143 throws CommandAbstractException {
144 final List<String> resultPaths = new ArrayList<String>();
145
146 if (!(pathWithWildcard.contains("*") || pathWithWildcard.contains("?") ||
147 pathWithWildcard.contains("~"))) {
148
149
150 if (getSession().getAuth().isBusinessPathValid(pathWithWildcard)) {
151 resultPaths.add(pathWithWildcard);
152 }
153 return resultPaths;
154 }
155
156 if (!FilesystemBasedDirJdkAbstract.ueApacheCommonsIo) {
157 throw new Reply553Exception("Wildcards in pathname is not allowed");
158 }
159 File wildcardFile;
160 final File rootFile;
161 if (!ISUNIX && isAbsolute(pathWithWildcard)) {
162 wildcardFile = new File(pathWithWildcard);
163 rootFile = getCorrespondingRoot(wildcardFile);
164 } else {
165 if (isAbsolute(pathWithWildcard)) {
166 rootFile = new File("/");
167 } else {
168 rootFile = new File(getSession().getAuth().getBaseDirectory());
169 }
170 wildcardFile = new File(rootFile, pathWithWildcard);
171 }
172
173 final List<String> subdirs = new ArrayList<String>();
174 while (wildcardFile != null) {
175 final File parent = wildcardFile.getParentFile();
176 if (parent == null) {
177 subdirs.add(0, wildcardFile.getPath());
178 break;
179 }
180 subdirs.add(0, wildcardFile.getName());
181 if (parent.equals(rootFile)) {
182
183 subdirs.add(0, parent.getPath());
184 break;
185 }
186 wildcardFile = parent;
187 }
188 List<File> basedPaths = new ArrayList<File>();
189
190 basedPaths.add(new File(subdirs.get(0)));
191 int i = 1;
192
193 while (i < subdirs.size()) {
194
195 final FileFilter fileFilter =
196 FilesystemBasedCommonsIo.getWildcardFileFilter(subdirs.get(i));
197 final List<File> newBasedPaths = new ArrayList<File>();
198
199 for (final File dir : basedPaths) {
200 if (dir.isDirectory()) {
201 newBasedPaths.addAll(Arrays.asList(dir.listFiles(fileFilter)));
202 }
203 }
204
205 basedPaths = newBasedPaths;
206 i++;
207 }
208
209 for (final File file : basedPaths) {
210 final String relativePath = getSession().getAuth().getRelativePath(
211 normalizePath(file.getAbsolutePath()));
212 final String newpath = validatePath(relativePath);
213 resultPaths.add(newpath);
214 }
215 return resultPaths;
216 }
217
218
219
220
221
222
223
224
225
226
227 protected final File getFileFromPath(final String path)
228 throws CommandAbstractException {
229 final String newdir = validatePath(path);
230 if (isAbsolute(newdir)) {
231 return new File(newdir);
232 }
233 final String truedir =
234 ((FilesystemBasedAuthImpl) getSession().getAuth()).getAbsolutePath(
235 newdir);
236 return new File(truedir);
237 }
238
239
240
241
242
243
244
245
246
247
248 protected final File getTrueFile(final String path)
249 throws CommandAbstractException {
250 checkIdentify();
251 final String newpath = consolidatePath(path);
252 final List<String> paths = wildcardFiles(normalizePath(newpath));
253 if (paths.size() != 1) {
254 throw new Reply550Exception(
255 "File not found: " + paths.size() + " founds");
256 }
257 String extDir = paths.get(0);
258 extDir = validatePath(extDir);
259 final File file = getFileFromPath(extDir);
260 if (!file.isFile()) {
261 throw new Reply550Exception("Path is not a file: " + path);
262 }
263 return file;
264 }
265
266
267
268
269
270
271
272
273 protected final String getRelativePath(final File file) {
274 return getSession().getAuth()
275 .getRelativePath(normalizePath(file.getAbsolutePath()));
276 }
277
278 @Override
279 public final boolean changeDirectory(final String path)
280 throws CommandAbstractException {
281 checkIdentify();
282 final String newpath = consolidatePath(path);
283 final List<String> paths = wildcardFiles(newpath);
284 if (paths.size() != 1) {
285 logger.warn("CD error: {}", newpath);
286 throw new Reply550Exception(
287 DIRECTORY_NOT_FOUND + paths.size() + " founds");
288 }
289 String extDir = paths.get(0);
290 extDir = validatePath(extDir);
291 if (isDirectory(extDir)) {
292 currentDir = extDir;
293 return true;
294 }
295 throw new Reply550Exception(DIRECTORY_NOT_FOUND + extDir);
296 }
297
298 @Override
299 public final boolean changeDirectoryNotChecked(final String path)
300 throws CommandAbstractException {
301 checkIdentify();
302 final String newpath = consolidatePath(path);
303 final List<String> paths = wildcardFiles(newpath);
304 if (paths.size() != 1) {
305 logger.warn("CD error: {}", newpath);
306 throw new Reply550Exception(
307 DIRECTORY_NOT_FOUND + paths.size() + " founds");
308 }
309 String extDir = paths.get(0);
310 extDir = validatePath(extDir);
311 currentDir = extDir;
312 return true;
313 }
314
315 @Override
316 public final String mkdir(final String directory)
317 throws CommandAbstractException {
318 checkIdentify();
319 final String newdirectory = consolidatePath(directory);
320 final File dir = new File(newdirectory);
321 final String parent = dir.getParentFile().getPath();
322 final List<String> paths = wildcardFiles(normalizePath(parent));
323 if (paths.size() != 1) {
324 throw new Reply550Exception(
325 "Base Directory not found: " + paths.size() + " founds");
326 }
327 String newDir = paths.get(0) + SEPARATOR + dir.getName();
328 newDir = validatePath(newDir);
329 final File newdir = getFileFromPath(newDir);
330 if (newdir.mkdir()) {
331 return newDir;
332 }
333 throw new Reply550Exception("Cannot create directory " + newDir);
334 }
335
336 @Override
337 public final String rmdir(final String directory)
338 throws CommandAbstractException {
339 checkIdentify();
340 final String newdirectory = consolidatePath(directory);
341 final List<String> paths = wildcardFiles(normalizePath(newdirectory));
342 if (paths.size() != 1) {
343 throw new Reply550Exception(
344 DIRECTORY_NOT_FOUND + paths.size() + " founds");
345 }
346 String extDir = paths.get(0);
347 extDir = validatePath(extDir);
348 final File dir = getFileFromPath(extDir);
349 if (dir.delete()) {
350 return extDir;
351 }
352 throw new Reply550Exception("Cannot delete directory " + extDir);
353 }
354
355 @Override
356 public final boolean isDirectory(final String path)
357 throws CommandAbstractException {
358 checkIdentify();
359 final File dir = getFileFromPath(path);
360 return dir.isDirectory();
361 }
362
363 @Override
364 public final boolean isFile(final String path)
365 throws CommandAbstractException {
366 checkIdentify();
367 return getFileFromPath(path).isFile();
368 }
369
370 @Override
371 public final String getModificationTime(final String path)
372 throws CommandAbstractException {
373 checkIdentify();
374 final File file = getFileFromPath(path);
375 if (file.exists()) {
376 return getModificationTime(file);
377 }
378 throw new Reply550Exception('"' + path + "\" does not exist");
379 }
380
381
382
383
384
385
386
387
388 protected final String getModificationTime(final File file) {
389 final long mstime = file.lastModified();
390 final Calendar calendar = Calendar.getInstance();
391 calendar.setTimeInMillis(mstime);
392 final int year = calendar.get(Calendar.YEAR);
393 final int month = calendar.get(Calendar.MONTH) + 1;
394 final int day = calendar.get(Calendar.DAY_OF_MONTH);
395 final int hour = calendar.get(Calendar.HOUR_OF_DAY);
396 final int minute = calendar.get(Calendar.MINUTE);
397 final int second = calendar.get(Calendar.SECOND);
398 final int ms = calendar.get(Calendar.MILLISECOND);
399 final StringBuilder sb = new StringBuilder(18);
400 sb.append(year);
401 if (month < 10) {
402 sb.append(0);
403 }
404 sb.append(month);
405 if (day < 10) {
406 sb.append(0);
407 }
408 sb.append(day);
409 if (hour < 10) {
410 sb.append(0);
411 }
412 sb.append(hour);
413 if (minute < 10) {
414 sb.append(0);
415 }
416 sb.append(minute);
417 if (second < 10) {
418 sb.append(0);
419 }
420 sb.append(second).append('.');
421 if (ms < 10) {
422 sb.append(0);
423 }
424 if (ms < 100) {
425 sb.append(0);
426 }
427 sb.append(ms);
428 return sb.toString();
429 }
430
431 @Override
432 public final List<String> list(final String path)
433 throws CommandAbstractException {
434 checkIdentify();
435
436 String newpath = path;
437 if (ParametersChecker.isEmpty(newpath)) {
438 newpath = currentDir;
439 }
440 if (newpath.startsWith("-a") || newpath.startsWith("-A")) {
441 final String[] args = newpath.split(" ");
442 if (args.length > 1) {
443 newpath = args[1];
444 } else {
445 newpath = currentDir;
446 }
447 }
448 newpath = consolidatePath(newpath);
449 logger.debug("debug: {}", newpath);
450 final List<String> paths = wildcardFiles(newpath);
451 if (paths.isEmpty()) {
452 throw new Reply550Exception("No files found");
453 }
454
455 final List<String> newPaths = new ArrayList<String>();
456 for (final String file : paths) {
457 final File dir = getFileFromPath(file);
458 if (dir.exists()) {
459 if (dir.isDirectory()) {
460 final String[] files = dir.list();
461 for (final String finalFile : files) {
462 final String relativePath =
463 getSession().getAuth().getRelativePath(finalFile);
464 newPaths.add(relativePath);
465 }
466 } else {
467 newPaths.add(file);
468 }
469 }
470 }
471 return newPaths;
472 }
473
474 @Override
475 public final List<String> listFull(final String path, final boolean lsFormat)
476 throws CommandAbstractException {
477 checkIdentify();
478 boolean listAllFiles = false;
479 String newpath = path;
480 if (ParametersChecker.isEmpty(newpath)) {
481 newpath = currentDir;
482 }
483 if (newpath.startsWith("-a") || newpath.startsWith("-A")) {
484 final String[] args = newpath.split(" ");
485 if (args.length > 1) {
486 newpath = args[1];
487 } else {
488 newpath = currentDir;
489 }
490 listAllFiles = true;
491 }
492 newpath = consolidatePath(newpath);
493
494 final List<String> paths = wildcardFiles(newpath);
495 if (paths.isEmpty()) {
496 throw new Reply550Exception("No files found");
497 }
498
499 final List<String> newPaths = new ArrayList<String>();
500 for (final String file : paths) {
501 final File dir = getFileFromPath(file);
502 if (dir.exists()) {
503 if (dir.isDirectory()) {
504 final File[] files = dir.listFiles();
505 for (final File finalFile : files) {
506 if (lsFormat) {
507 newPaths.add(lsInfo(finalFile));
508 } else {
509 newPaths.add(mlsxInfo(finalFile));
510 }
511 }
512 } else {
513 if (lsFormat) {
514 newPaths.add(lsInfo(dir));
515 } else {
516 newPaths.add(mlsxInfo(dir));
517 }
518 }
519 }
520 }
521 if (listAllFiles) {
522 final File dir = new File(getFileFromPath(newpath), SEPARATOR + "..");
523 if (lsFormat) {
524 newPaths.add(lsInfo(dir));
525 } else {
526 newPaths.add(mlsxInfo(dir));
527 }
528 }
529 return newPaths;
530 }
531
532 @Override
533 public final String fileFull(final String path, final boolean lsFormat)
534 throws CommandAbstractException {
535 checkIdentify();
536 final String newpath = consolidatePath(path);
537 final List<String> paths = wildcardFiles(normalizePath(newpath));
538 if (paths.size() != 1) {
539 throw new Reply550Exception("No files found " + paths.size() + " founds");
540 }
541 final File file = getFileFromPath(paths.get(0));
542 if (file.exists()) {
543 if (lsFormat) {
544 return "Listing of \"" + paths.get(0) + "\"\n" + lsInfo(file) +
545 "\nEnd of listing";
546 }
547 return "Listing of \"" + paths.get(0) + "\"\n" + mlsxInfo(file) +
548 "\nEnd of listing";
549 }
550 return "No file with name \"" + path + '"';
551 }
552
553
554
555
556
557
558 protected final boolean isFullTime() {
559 return false;
560 }
561
562
563
564
565
566
567 protected final String lsInfo(final File file) {
568
569
570
571 final StringBuilder builder =
572 new StringBuilder().append(file.isDirectory()? 'd' : '-')
573 .append(file.canRead()? 'r' : '-')
574 .append(file.canWrite()? 'w' : '-');
575 if (filesystemBasedFtpDirJdk != null) {
576 builder.append(filesystemBasedFtpDirJdk.canExecute(file)? 'x' : '-');
577 } else {
578 builder.append('-');
579 }
580
581 builder.append("---").append("---").append(' ').append("1 ")
582 .append("anybody\t")
583 .append("anygroup\t")
584 .append(file.length())
585 .append('\t');
586 final long lastmod = file.lastModified();
587 final String fmt;
588
589 final long currentTime = System.currentTimeMillis();
590 if (currentTime > lastmod + 6L * 30L * 24L * 60L * 60L * 1000L
591 || currentTime < lastmod - 60L * 60L * 1000L) {
592
593
594
595
596
597
598
599 fmt = "MMM dd yyyy";
600 } else {
601 fmt = "MMM dd HH:mm";
602 }
603 final SimpleDateFormat dateFormat =
604 (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG,
605 DateFormat.LONG,
606 Locale.ENGLISH);
607 dateFormat.applyPattern(fmt);
608 builder.append(dateFormat.format(new Date(lastmod)))
609 .append('\t').append(file.getName());
610 return builder.toString();
611 }
612
613
614
615
616
617
618 protected final String mlsxInfo(final File file) {
619
620 final StringBuilder builder = new StringBuilder(" ");
621 if (getOptsMLSx().getOptsSize() == 1) {
622 builder.append("Size=").append(file.length()).append(';');
623 }
624 if (getOptsMLSx().getOptsModify() == 1) {
625 builder.append("Modify=").append(getModificationTime(file)).append(';');
626 }
627 if (getOptsMLSx().getOptsType() == 1) {
628 builder.append("Type=");
629 try {
630 if (getFileFromPath(currentDir).equals(file)) {
631 builder.append("cdir");
632 } else {
633 if (file.isDirectory()) {
634 builder.append("dir");
635 } else {
636 builder.append("file");
637 }
638 }
639 } catch (final CommandAbstractException e) {
640 if (file.isDirectory()) {
641 builder.append("dir");
642 } else {
643 builder.append("file");
644 }
645 }
646 builder.append(';');
647 }
648 if (getOptsMLSx().getOptsPerm() == 1) {
649 builder.append("Perm=");
650 if (file.isFile()) {
651 if (file.canWrite()) {
652 builder.append('a').append('d').append('f').append('w');
653 }
654 if (file.canRead()) {
655 builder.append('r');
656 }
657 } else {
658
659 if (file.canWrite()) {
660 builder.append('c');
661 try {
662 if (validatePath(file) != null) {
663 builder.append('d').append('m').append('p');
664 }
665 } catch (final CommandAbstractException ignored) {
666
667 }
668 }
669 if (file.canRead()) {
670 builder.append('l').append('e');
671 }
672 }
673 builder.append(';');
674 }
675
676 builder.append(' ').append(file.getName());
677 return builder.toString();
678 }
679
680 @Override
681 public final long getFreeSpace() throws CommandAbstractException {
682 checkIdentify();
683 final File directory = getFileFromPath(currentDir);
684 if (filesystemBasedFtpDirJdk != null) {
685 return filesystemBasedFtpDirJdk.getFreeSpace(directory);
686 } else {
687 return Integer.MAX_VALUE;
688 }
689 }
690
691 @Override
692 public FileInterface setUniqueFile() throws CommandAbstractException {
693 checkIdentify();
694 final File file;
695 try {
696 file = File.createTempFile(getSession().getAuth().getUser(),
697 session.getUniqueExtension(),
698 getFileFromPath(currentDir));
699 } catch (final IOException e) {
700 throw new Reply550Exception("Cannot create unique file");
701 }
702 final String currentFile = getRelativePath(file);
703 return newFile(normalizePath(currentFile), false);
704 }
705
706 @Override
707 public final boolean canRead() throws CommandAbstractException {
708 checkIdentify();
709 return getFileFromPath(currentDir).canRead();
710 }
711
712 @Override
713 public final boolean canWrite() throws CommandAbstractException {
714 checkIdentify();
715 final File file = getFileFromPath(currentDir);
716 return file.canWrite();
717 }
718
719 @Override
720 public final boolean exists() throws CommandAbstractException {
721 checkIdentify();
722 return getFileFromPath(currentDir).exists();
723 }
724
725 @Override
726 public final long getCRC(final String path) throws CommandAbstractException {
727 final File file = getTrueFile(path);
728 FileInputStream fis = null;
729 CheckedInputStream cis = null;
730 try {
731 try {
732
733 fis = new FileInputStream(file);
734 cis = new CheckedInputStream(fis, new CRC32());
735 } catch (final FileNotFoundException e) {
736 throw new Reply550Exception("File not found: " + path);
737 }
738 final byte[] buf = new byte[ZERO_COPY_CHUNK_SIZE];
739 while (cis.read(buf) >= 0) {
740
741 }
742 return cis.getChecksum().getValue();
743 } catch (final IOException e) {
744 throw new Reply550Exception(ERROR_WHILE_READING_FILE + path);
745 } finally {
746 FileUtils.close(cis);
747 FileUtils.close(fis);
748 }
749 }
750
751 @Override
752 public final byte[] getMD5(final String path)
753 throws CommandAbstractException {
754 return getDigest(path, DigestAlgo.MD5.name());
755 }
756
757 @Override
758 public final byte[] getSHA1(final String path)
759 throws CommandAbstractException {
760 return getDigest(path, DigestAlgo.SHA1.name());
761 }
762
763 @Override
764 public final byte[] getDigest(final String path, final String algo)
765 throws CommandAbstractException {
766 final DigestAlgo digestAlgo;
767 try {
768 digestAlgo = DigestAlgo.getFromName(algo);
769 } catch (final IllegalArgumentException e) {
770 throw new Reply553Exception("Algorithme unknown: " + algo);
771 }
772 final File file = getTrueFile(path);
773 try {
774 return FilesystemBasedDigest.getHash(file,
775 FilesystemBasedFileParameterImpl.useNio,
776 digestAlgo);
777 } catch (final IOException e1) {
778 throw new Reply550Exception(ERROR_WHILE_READING_FILE + path);
779 }
780 }
781
782 }