View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.commons.net.PrintCommandListener;
28  import org.apache.commons.net.ftp.FTPClient;
29  import org.apache.commons.net.ftp.FTPClientConfig;
30  import org.apache.commons.net.ftp.FTPReply;
31  import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory;
32  import org.apache.commons.vfs2.FileSystemException;
33  import org.apache.commons.vfs2.FileSystemOptions;
34  import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
35  
36  /**
37   * Create a FtpClient instance.
38   */
39  public final class FtpClientFactory
40  {
41      private FtpClientFactory()
42      {
43      }
44  
45      /**
46       * Creates a new connection to the server.
47       *
48       * @param hostname          The host name of the server.
49       * @param port              The port to connect to.
50       * @param username          The name of the user for authentication.
51       * @param password          The user's password.
52       * @param workingDirectory  The base directory.
53       * @param fileSystemOptions The FileSystemOptions.
54       * @return An FTPClient.
55       * @throws FileSystemException if an error occurs while connecting.
56       */
57      public static FTPClient createConnection(final String hostname, final int port,
58                                               final char[] username, final char[] password,
59                                               final String workingDirectory, final FileSystemOptions fileSystemOptions)
60          throws FileSystemException
61      {
62          final FtpConnectionFactory factory = new FtpConnectionFactory(FtpFileSystemConfigBuilder.getInstance());
63          return factory.createConnection(hostname, port, username, password, workingDirectory, fileSystemOptions);
64      }
65  
66      /** Connection Factory, used to configure the FTPClient. */
67      public static final class FtpConnectionFactory extends ConnectionFactory<FTPClient, FtpFileSystemConfigBuilder>
68      {
69          private FtpConnectionFactory(final FtpFileSystemConfigBuilder builder)
70          {
71              super(builder);
72          }
73  
74          @Override
75          protected FTPClient createClient(final FileSystemOptions fileSystemOptions)
76          {
77              return new FTPClient();
78          }
79  
80          @Override
81          protected void setupOpenConnection(final FTPClient client, final FileSystemOptions fileSystemOptions)
82          {
83              // nothing to do for FTP
84          }
85      }
86  
87      /** Abstract Factory, used to configure different FTPClients. */
88      public abstract static class ConnectionFactory<C extends FTPClient, B extends FtpFileSystemConfigBuilder>
89      {
90          private static final char[] ANON_CHAR_ARRAY = "anonymous".toCharArray();
91          private static final int BUFSZ = 40;
92          private final Log log = LogFactory.getLog(getClass());
93  
94          protected B builder;
95  
96          protected ConnectionFactory(final B builder)
97          {
98              this.builder = builder;
99          }
100 
101         public C createConnection(final String hostname, final int port, char[] username, char[] password,
102             final String workingDirectory, final FileSystemOptions fileSystemOptions) throws FileSystemException
103         {
104             // Determine the username and password to use
105             if (username == null)
106             {
107                 username = ANON_CHAR_ARRAY;
108             }
109 
110             if (password == null)
111             {
112                 password = ANON_CHAR_ARRAY;
113             }
114 
115             try
116             {
117                 final C client = createClient(fileSystemOptions);
118 
119                 if (log.isDebugEnabled())
120                 {
121                     final Writer writer = new StringWriter(1024)
122                     {
123                         @Override
124                         public void flush()
125                         {
126                             final StringBuffer buffer = getBuffer();
127                             String message = buffer.toString();
128                             if (message.toUpperCase().startsWith("PASS ") && message.length() > 5)
129                             {
130                                 message = "PASS ***";
131                             }
132                             log.debug(message);
133                             buffer.setLength(0);
134                         }
135                     };
136                     client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(writer)));
137                 }
138 
139                 configureClient(fileSystemOptions, client);
140 
141                 final FTPFileEntryParserFactory myFactory =
142                     builder.getEntryParserFactory(fileSystemOptions);
143                 if (myFactory != null)
144                 {
145                     client.setParserFactory(myFactory);
146                 }
147 
148                 Boolean remoteVerification = builder.getRemoteVerification(fileSystemOptions);
149                 if (remoteVerification != null)
150                 {
151                     client.setRemoteVerificationEnabled(remoteVerification.booleanValue());
152                 }
153 
154                 try
155                 {
156                     // Set connect timeout
157                     final Integer connectTimeout = builder.getConnectTimeout(fileSystemOptions);
158                     if (connectTimeout != null)
159                     {
160                         client.setDefaultTimeout(connectTimeout.intValue());
161                     }
162 
163                     final String controlEncoding = builder.getControlEncoding(fileSystemOptions);
164                     if (controlEncoding != null)
165                     {
166                         client.setControlEncoding(controlEncoding);
167                     }
168 
169                     final Proxy proxy = builder.getProxy(fileSystemOptions);
170                     if (proxy != null)
171                     {
172                         client.setProxy(proxy);
173                     }
174 
175                     client.connect(hostname, port);
176 
177                     final int reply = client.getReplyCode();
178                     if (!FTPReply.isPositiveCompletion(reply))
179                     {
180                         throw new FileSystemException("vfs.provider.ftp/connect-rejected.error", hostname);
181                     }
182 
183                     // Login
184                     if (!client.login(
185                         UserAuthenticatorUtils.toString(username),
186                         UserAuthenticatorUtils.toString(password)))
187                     {
188                         throw new FileSystemException("vfs.provider.ftp/login.error",
189                             hostname, UserAuthenticatorUtils.toString(username));
190                     }
191 
192                     FtpFileType fileType = builder.getFileType(fileSystemOptions);
193                     if (fileType == null)
194                     {
195                         fileType = FtpFileType.BINARY;
196                     }
197                     // Set binary mode
198                     if (!client.setFileType(fileType.getValue()))
199                     {
200                         throw new FileSystemException("vfs.provider.ftp/set-file-type.error", fileType);
201                     }
202 
203                     // Set dataTimeout value
204                     final Integer dataTimeout = builder.getDataTimeout(fileSystemOptions);
205                     if (dataTimeout != null)
206                     {
207                         client.setDataTimeout(dataTimeout.intValue());
208                     }
209 
210                     final Integer socketTimeout = builder.getSoTimeout(fileSystemOptions);
211                     if (socketTimeout != null)
212                     {
213                         client.setSoTimeout(socketTimeout.intValue());
214                     }
215 
216                     final Boolean userDirIsRoot = builder.getUserDirIsRoot(fileSystemOptions);
217                     if (workingDirectory != null && (userDirIsRoot == null || !userDirIsRoot.booleanValue()))
218                     {
219                         if (!client.changeWorkingDirectory(workingDirectory))
220                         {
221                             throw new FileSystemException("vfs.provider.ftp/change-work-directory.error",
222                                                           workingDirectory);
223                         }
224                     }
225 
226                     final Boolean passiveMode = builder.getPassiveMode(fileSystemOptions);
227                     if (passiveMode != null && passiveMode.booleanValue())
228                     {
229                         client.enterLocalPassiveMode();
230                     }
231 
232                     setupOpenConnection(client, fileSystemOptions);
233                 }
234                 catch (final IOException e)
235                 {
236                     if (client.isConnected())
237                     {
238                         client.disconnect();
239                     }
240                     throw e;
241                 }
242 
243                 return client;
244             }
245             catch (final Exception exc)
246             {
247                 throw new FileSystemException("vfs.provider.ftp/connect.error", exc, hostname);
248             }
249         }
250 
251         protected abstract C createClient(FileSystemOptions fileSystemOptions) throws FileSystemException;
252         protected abstract void setupOpenConnection(C client, FileSystemOptions fileSystemOptions) throws IOException;
253 
254         private void configureClient(final FileSystemOptions fileSystemOptions, final C client)
255         {
256             final String key = builder.getEntryParser(fileSystemOptions);
257             if (key != null)
258             {
259                 final FTPClientConfig config = new FTPClientConfig(key);
260 
261                 final String serverLanguageCode =
262                     builder.getServerLanguageCode(fileSystemOptions);
263                 if (serverLanguageCode != null)
264                 {
265                     config.setServerLanguageCode(serverLanguageCode);
266                 }
267                 final String defaultDateFormat =
268                     builder.getDefaultDateFormat(fileSystemOptions);
269                 if (defaultDateFormat != null)
270                 {
271                     config.setDefaultDateFormatStr(defaultDateFormat);
272                 }
273                 final String recentDateFormat =
274                     builder.getRecentDateFormat(fileSystemOptions);
275                 if (recentDateFormat != null)
276                 {
277                     config.setRecentDateFormatStr(recentDateFormat);
278                 }
279                 final String serverTimeZoneId =
280                     builder.getServerTimeZoneId(fileSystemOptions);
281                 if (serverTimeZoneId != null)
282                 {
283                     config.setServerTimeZoneId(serverTimeZoneId);
284                 }
285                 final String[] shortMonthNames =
286                     builder.getShortMonthNames(fileSystemOptions);
287                 if (shortMonthNames != null)
288                 {
289                     final StringBuilder shortMonthNamesStr = new StringBuilder(BUFSZ);
290                     for (final String shortMonthName : shortMonthNames)
291                     {
292                         if (shortMonthNamesStr.length() > 0)
293                         {
294                             shortMonthNamesStr.append("|");
295                         }
296                         shortMonthNamesStr.append(shortMonthName);
297                     }
298                     config.setShortMonthNames(shortMonthNamesStr.toString());
299                 }
300 
301                 client.configure(config);
302             }
303         }
304     }
305 }