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