1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.ftp;
18
19 import java.io.IOException;
20 import java.io.PrintWriter;
21 import java.io.StringWriter;
22 import java.io.Writer;
23 import java.net.Proxy;
24 import java.time.Duration;
25
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.commons.lang3.time.DurationUtils;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.commons.net.PrintCommandListener;
31 import org.apache.commons.net.ftp.FTPClient;
32 import org.apache.commons.net.ftp.FTPClientConfig;
33 import org.apache.commons.net.ftp.FTPReply;
34 import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory;
35 import org.apache.commons.vfs2.FileSystemException;
36 import org.apache.commons.vfs2.FileSystemOptions;
37 import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
38
39
40
41
42 public final class FtpClientFactory {
43
44
45
46
47
48
49
50
51
52 public abstract static class ConnectionFactory<C extends FTPClient, B extends FtpFileSystemConfigBuilder> {
53 private static final char[] ANON_CHAR_ARRAY = "anonymous".toCharArray();
54 private static final int BUFSZ = 40;
55 private final Log log = LogFactory.getLog(getClass());
56
57 protected B builder;
58
59 protected ConnectionFactory(final B builder) {
60 this.builder = builder;
61 }
62
63 private void configureClient(final FileSystemOptions fileSystemOptions, final C client) {
64 final String key = builder.getEntryParser(fileSystemOptions);
65 if (key != null) {
66 final FTPClientConfig config = new FTPClientConfig(key);
67
68 final String serverLanguageCode = builder.getServerLanguageCode(fileSystemOptions);
69 if (serverLanguageCode != null) {
70 config.setServerLanguageCode(serverLanguageCode);
71 }
72 final String defaultDateFormat = builder.getDefaultDateFormat(fileSystemOptions);
73 if (defaultDateFormat != null) {
74 config.setDefaultDateFormatStr(defaultDateFormat);
75 }
76 final String recentDateFormat = builder.getRecentDateFormat(fileSystemOptions);
77 if (recentDateFormat != null) {
78 config.setRecentDateFormatStr(recentDateFormat);
79 }
80 final String serverTimeZoneId = builder.getServerTimeZoneId(fileSystemOptions);
81 if (serverTimeZoneId != null) {
82 config.setServerTimeZoneId(serverTimeZoneId);
83 }
84 final String[] shortMonthNames = builder.getShortMonthNames(fileSystemOptions);
85 if (shortMonthNames != null) {
86 final StringBuilder shortMonthNamesStr = new StringBuilder(BUFSZ);
87 for (final String shortMonthName : shortMonthNames) {
88 if (!StringUtils.isEmpty(shortMonthNamesStr)) {
89 shortMonthNamesStr.append("|");
90 }
91 shortMonthNamesStr.append(shortMonthName);
92 }
93 config.setShortMonthNames(shortMonthNamesStr.toString());
94 }
95
96 client.configure(config);
97 }
98 }
99
100 protected abstract C createClient(FileSystemOptions fileSystemOptions) throws FileSystemException;
101
102 public C createConnection(final String hostname, final int port, char[] username, char[] password,
103 final String workingDirectory, final FileSystemOptions fileSystemOptions) throws FileSystemException {
104
105 if (username == null) {
106 username = ANON_CHAR_ARRAY;
107 }
108
109 if (password == null) {
110 password = ANON_CHAR_ARRAY;
111 }
112
113 try {
114 final C client = createClient(fileSystemOptions);
115
116 if (log.isDebugEnabled()) {
117 final Writer writer = new StringWriter(1024) {
118 @Override
119 public void flush() {
120 final StringBuffer buffer = getBuffer();
121 String message = buffer.toString();
122 if (message.toUpperCase().startsWith("PASS ") && message.length() > 5) {
123 message = "PASS ***";
124 }
125 log.debug(message);
126 buffer.setLength(0);
127 }
128 };
129 client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(writer)));
130 }
131
132 configureClient(fileSystemOptions, client);
133
134 final FTPFileEntryParserFactory myFactory = builder.getEntryParserFactory(fileSystemOptions);
135 if (myFactory != null) {
136 client.setParserFactory(myFactory);
137 }
138
139 final Boolean remoteVerification = builder.getRemoteVerification(fileSystemOptions);
140 if (remoteVerification != null) {
141 client.setRemoteVerificationEnabled(remoteVerification.booleanValue());
142 }
143
144 try {
145 final Duration connectTimeout = builder.getConnectTimeoutDuration(fileSystemOptions);
146 if (connectTimeout != null) {
147 client.setDefaultTimeout(DurationUtils.toMillisInt(connectTimeout));
148 }
149
150 final String controlEncoding = builder.getControlEncoding(fileSystemOptions);
151 if (controlEncoding != null) {
152 client.setControlEncoding(controlEncoding);
153 }
154
155 final Boolean autodetectUTF8 = builder.getAutodetectUtf8(fileSystemOptions);
156 if (autodetectUTF8 != null) {
157 client.setAutodetectUTF8(autodetectUTF8);
158 }
159
160 final Proxy proxy = builder.getProxy(fileSystemOptions);
161 if (proxy != null) {
162 client.setProxy(proxy);
163 }
164
165 client.connect(hostname, port);
166
167 final int reply = client.getReplyCode();
168 if (!FTPReply.isPositiveCompletion(reply)) {
169 throw new FileSystemException("vfs.provider.ftp/connect-rejected.error", hostname);
170 }
171
172
173 if (!client.login(UserAuthenticatorUtils.toString(username),
174 UserAuthenticatorUtils.toString(password))) {
175 throw new FileSystemException("vfs.provider.ftp/login.error", hostname,
176 UserAuthenticatorUtils.toString(username));
177 }
178
179 FtpFileType fileType = builder.getFileType(fileSystemOptions);
180 if (fileType == null) {
181 fileType = FtpFileType.BINARY;
182 }
183
184 if (!client.setFileType(fileType.getValue())) {
185 throw new FileSystemException("vfs.provider.ftp/set-file-type.error", fileType);
186 }
187
188
189 final Duration dataTimeout = builder.getDataTimeoutDuration(fileSystemOptions);
190 if (dataTimeout != null) {
191 client.setDataTimeout(DurationUtils.toMillisInt(dataTimeout));
192 }
193
194 final Duration socketTimeout = builder.getSoTimeoutDuration(fileSystemOptions);
195 if (socketTimeout != null) {
196 client.setSoTimeout(DurationUtils.toMillisInt(socketTimeout));
197 }
198
199 final Duration controlKeepAliveTimeout = builder.getControlKeepAliveTimeout(fileSystemOptions);
200 if (controlKeepAliveTimeout != null) {
201
202 client.setControlKeepAliveTimeout(controlKeepAliveTimeout.getSeconds());
203 }
204
205 final Duration controlKeepAliveReplyTimeout = builder
206 .getControlKeepAliveReplyTimeout(fileSystemOptions);
207 if (controlKeepAliveReplyTimeout != null) {
208 client.setControlKeepAliveReplyTimeout((int) controlKeepAliveReplyTimeout.toMillis());
209 }
210
211 final Boolean userDirIsRoot = builder.getUserDirIsRoot(fileSystemOptions);
212 if ((workingDirectory != null && (userDirIsRoot == null || !userDirIsRoot.booleanValue())) && !client.changeWorkingDirectory(workingDirectory)) {
213 throw new FileSystemException("vfs.provider.ftp/change-work-directory.error",
214 workingDirectory);
215 }
216
217 final Boolean passiveMode = builder.getPassiveMode(fileSystemOptions);
218 if (passiveMode != null && passiveMode.booleanValue()) {
219 client.enterLocalPassiveMode();
220 }
221
222 setupOpenConnection(client, fileSystemOptions);
223 } catch (final IOException e) {
224 if (client.isConnected()) {
225 client.disconnect();
226 }
227 throw e;
228 }
229
230 return client;
231 } catch (final Exception exc) {
232 throw new FileSystemException("vfs.provider.ftp/connect.error", exc, hostname);
233 }
234 }
235
236 protected abstract void setupOpenConnection(C client, FileSystemOptions fileSystemOptions) throws IOException;
237 }
238
239
240 public static final class FtpConnectionFactory extends ConnectionFactory<FTPClient, FtpFileSystemConfigBuilder> {
241 private FtpConnectionFactory(final FtpFileSystemConfigBuilder builder) {
242 super(builder);
243 }
244
245 @Override
246 protected FTPClient createClient(final FileSystemOptions fileSystemOptions) {
247 return new FTPClient();
248 }
249
250 @Override
251 protected void setupOpenConnection(final FTPClient client, final FileSystemOptions fileSystemOptions) {
252
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268 public static FTPClient createConnection(final String hostname, final int port, final char[] username,
269 final char[] password, final String workingDirectory, final FileSystemOptions fileSystemOptions)
270 throws FileSystemException {
271 final FtpConnectionFactory factory = new FtpConnectionFactory(FtpFileSystemConfigBuilder.getInstance());
272 return factory.createConnection(hostname, port, username, password, workingDirectory, fileSystemOptions);
273 }
274
275 private FtpClientFactory() {
276 }
277 }