Add session to cache with both hostname and address.
[cyberduck.git] / source / ch / cyberduck / core / ftp / FTPListResponseReader.java
blob58acefc5a604814a96787a96243d097a7fc6146d
1 package ch.cyberduck.core.ftp;
3 /*
4 * Copyright (c) 2002-2013 David Kocher. All rights reserved.
5 * http://cyberduck.ch/
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch
20 import ch.cyberduck.core.AttributedList;
21 import ch.cyberduck.core.ListProgressListener;
22 import ch.cyberduck.core.Path;
23 import ch.cyberduck.core.PathNormalizer;
24 import ch.cyberduck.core.Permission;
25 import ch.cyberduck.core.exception.ConnectionCanceledException;
26 import ch.cyberduck.core.ftp.parser.FTPExtendedFile;
28 import org.apache.commons.net.ftp.FTPFile;
29 import org.apache.commons.net.ftp.FTPFileEntryParser;
30 import org.apache.log4j.Logger;
32 import java.io.IOException;
33 import java.util.Calendar;
34 import java.util.EnumSet;
35 import java.util.List;
37 /**
38 * @version $Id$
40 public class FTPListResponseReader implements FTPDataResponseReader {
41 private static final Logger log = Logger.getLogger(FTPListResponseReader.class);
43 private FTPFileEntryParser parser;
45 private boolean lenient;
47 public FTPListResponseReader(final FTPFileEntryParser parser) {
48 this(parser, false);
51 public FTPListResponseReader(final FTPFileEntryParser parser, final boolean lenient) {
52 this.parser = parser;
53 this.lenient = lenient;
56 @Override
57 public AttributedList<Path> read(final Path directory, final List<String> replies, final ListProgressListener listener)
58 throws IOException, FTPInvalidListException, ConnectionCanceledException {
59 final AttributedList<Path> children = new AttributedList<Path>();
60 // At least one entry successfully parsed
61 boolean success = false;
62 // Call hook for those implementors which need to perform some action upon the list after it has been created
63 // from the server stream, but before any clients see the list
64 parser.preParse(replies);
65 for(String line : replies) {
66 final FTPFile f = parser.parseFTPEntry(line);
67 if(null == f) {
68 continue;
70 final String name = f.getName();
71 if(!success) {
72 if(lenient) {
73 // Workaround for #2410. STAT only returns ls of directory itself
74 // Workaround for #2434. STAT of symbolic link directory only lists the directory itself.
75 if(directory.getName().equals(name)) {
76 log.warn(String.format("Skip %s matching parent directory name", f.getName()));
77 continue;
79 if(name.contains(String.valueOf(Path.DELIMITER))) {
80 if(!name.startsWith(directory.getAbsolute() + Path.DELIMITER)) {
81 // Workaround for #2434.
82 log.warn(String.format("Skip %s with delimiter in name", name));
83 continue;
88 success = true;
89 if(name.equals(".") || name.equals("..")) {
90 if(log.isDebugEnabled()) {
91 log.debug(String.format("Skip %s", f.getName()));
93 continue;
95 final Path parsed = new Path(directory, PathNormalizer.name(name), f.getType() == FTPFile.DIRECTORY_TYPE ? EnumSet.of(Path.Type.directory) : EnumSet.of(Path.Type.file));
96 switch(f.getType()) {
97 case FTPFile.SYMBOLIC_LINK_TYPE:
98 parsed.setType(EnumSet.of(Path.Type.file, Path.Type.symboliclink));
99 // Symbolic link target may be an absolute or relative path
100 final String target = f.getLink();
101 if(target.startsWith(String.valueOf(Path.DELIMITER))) {
102 parsed.setSymlinkTarget(new Path(target, EnumSet.of(Path.Type.file)));
104 else {
105 parsed.setSymlinkTarget(new Path(String.format("%s/%s", directory.getAbsolute(), target),
106 EnumSet.of(Path.Type.file)));
108 break;
110 if(parsed.isFile()) {
111 parsed.attributes().setSize(f.getSize());
113 parsed.attributes().setOwner(f.getUser());
114 parsed.attributes().setGroup(f.getGroup());
115 Permission.Action u = Permission.Action.none;
116 if(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)) {
117 u = u.or(Permission.Action.read);
119 if(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)) {
120 u = u.or(Permission.Action.write);
122 if(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)) {
123 u = u.or(Permission.Action.execute);
125 Permission.Action g = Permission.Action.none;
126 if(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)) {
127 g = g.or(Permission.Action.read);
129 if(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)) {
130 g = g.or(Permission.Action.write);
132 if(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)) {
133 g = g.or(Permission.Action.execute);
135 Permission.Action o = Permission.Action.none;
136 if(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)) {
137 o = o.or(Permission.Action.read);
139 if(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)) {
140 o = o.or(Permission.Action.write);
142 if(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)) {
143 o = o.or(Permission.Action.execute);
145 final Permission permission = new Permission(u, g, o);
146 if(f instanceof FTPExtendedFile) {
147 permission.setSetuid(((FTPExtendedFile) f).isSetuid());
148 permission.setSetgid(((FTPExtendedFile) f).isSetgid());
149 permission.setSticky(((FTPExtendedFile) f).isSticky());
151 parsed.attributes().setPermission(permission);
152 final Calendar timestamp = f.getTimestamp();
153 if(timestamp != null) {
154 parsed.attributes().setModificationDate(timestamp.getTimeInMillis());
156 children.add(parsed);
157 listener.chunk(directory, children);
159 if(!success) {
160 throw new FTPInvalidListException(children);
162 return children;