001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.vfs2.provider.ftp; 018 019import java.io.IOException; 020import java.io.PrintWriter; 021import java.io.StringWriter; 022import java.io.Writer; 023import java.net.Proxy; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.apache.commons.net.PrintCommandListener; 028import org.apache.commons.net.ftp.FTPClient; 029import org.apache.commons.net.ftp.FTPClientConfig; 030import org.apache.commons.net.ftp.FTPReply; 031import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory; 032import org.apache.commons.vfs2.FileSystemException; 033import org.apache.commons.vfs2.FileSystemOptions; 034import org.apache.commons.vfs2.util.UserAuthenticatorUtils; 035 036/** 037 * Create a FtpClient instance. 038 */ 039public final class FtpClientFactory { 040 private FtpClientFactory() { 041 } 042 043 /** 044 * Creates a new connection to the server. 045 * 046 * @param hostname The host name of the server. 047 * @param port The port to connect to. 048 * @param username The name of the user for authentication. 049 * @param password The user's password. 050 * @param workingDirectory The base directory. 051 * @param fileSystemOptions The FileSystemOptions. 052 * @return An FTPClient. 053 * @throws FileSystemException if an error occurs while connecting. 054 */ 055 public static FTPClient createConnection(final String hostname, final int port, final char[] username, 056 final char[] password, final String workingDirectory, final FileSystemOptions fileSystemOptions) 057 throws FileSystemException { 058 final FtpConnectionFactory factory = new FtpConnectionFactory(FtpFileSystemConfigBuilder.getInstance()); 059 return factory.createConnection(hostname, port, username, password, workingDirectory, fileSystemOptions); 060 } 061 062 /** Connection Factory, used to configure the FTPClient. */ 063 public static final class FtpConnectionFactory extends ConnectionFactory<FTPClient, FtpFileSystemConfigBuilder> { 064 private FtpConnectionFactory(final FtpFileSystemConfigBuilder builder) { 065 super(builder); 066 } 067 068 @Override 069 protected FTPClient createClient(final FileSystemOptions fileSystemOptions) { 070 return new FTPClient(); 071 } 072 073 @Override 074 protected void setupOpenConnection(final FTPClient client, final FileSystemOptions fileSystemOptions) { 075 // nothing to do for FTP 076 } 077 } 078 079 /** Abstract Factory, used to configure different FTPClients. */ 080 public abstract static class ConnectionFactory<C extends FTPClient, B extends FtpFileSystemConfigBuilder> { 081 private static final char[] ANON_CHAR_ARRAY = "anonymous".toCharArray(); 082 private static final int BUFSZ = 40; 083 private final Log log = LogFactory.getLog(getClass()); 084 085 protected B builder; 086 087 protected ConnectionFactory(final B builder) { 088 this.builder = builder; 089 } 090 091 public C createConnection(final String hostname, final int port, char[] username, char[] password, 092 final String workingDirectory, final FileSystemOptions fileSystemOptions) throws FileSystemException { 093 // Determine the username and password to use 094 if (username == null) { 095 username = ANON_CHAR_ARRAY; 096 } 097 098 if (password == null) { 099 password = ANON_CHAR_ARRAY; 100 } 101 102 try { 103 final C client = createClient(fileSystemOptions); 104 105 if (log.isDebugEnabled()) { 106 final Writer writer = new StringWriter(1024) { 107 @Override 108 public void flush() { 109 final StringBuffer buffer = getBuffer(); 110 String message = buffer.toString(); 111 if (message.toUpperCase().startsWith("PASS ") && message.length() > 5) { 112 message = "PASS ***"; 113 } 114 log.debug(message); 115 buffer.setLength(0); 116 } 117 }; 118 client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(writer))); 119 } 120 121 configureClient(fileSystemOptions, client); 122 123 final FTPFileEntryParserFactory myFactory = builder.getEntryParserFactory(fileSystemOptions); 124 if (myFactory != null) { 125 client.setParserFactory(myFactory); 126 } 127 128 final Boolean remoteVerification = builder.getRemoteVerification(fileSystemOptions); 129 if (remoteVerification != null) { 130 client.setRemoteVerificationEnabled(remoteVerification.booleanValue()); 131 } 132 133 try { 134 // Set connect timeout 135 final Integer connectTimeout = builder.getConnectTimeout(fileSystemOptions); 136 if (connectTimeout != null) { 137 client.setDefaultTimeout(connectTimeout.intValue()); 138 } 139 140 final String controlEncoding = builder.getControlEncoding(fileSystemOptions); 141 if (controlEncoding != null) { 142 client.setControlEncoding(controlEncoding); 143 } 144 145 final Proxy proxy = builder.getProxy(fileSystemOptions); 146 if (proxy != null) { 147 client.setProxy(proxy); 148 } 149 150 client.connect(hostname, port); 151 152 final int reply = client.getReplyCode(); 153 if (!FTPReply.isPositiveCompletion(reply)) { 154 throw new FileSystemException("vfs.provider.ftp/connect-rejected.error", hostname); 155 } 156 157 // Login 158 if (!client.login(UserAuthenticatorUtils.toString(username), 159 UserAuthenticatorUtils.toString(password))) { 160 throw new FileSystemException("vfs.provider.ftp/login.error", hostname, 161 UserAuthenticatorUtils.toString(username)); 162 } 163 164 FtpFileType fileType = builder.getFileType(fileSystemOptions); 165 if (fileType == null) { 166 fileType = FtpFileType.BINARY; 167 } 168 // Set binary mode 169 if (!client.setFileType(fileType.getValue())) { 170 throw new FileSystemException("vfs.provider.ftp/set-file-type.error", fileType); 171 } 172 173 // Set dataTimeout value 174 final Integer dataTimeout = builder.getDataTimeout(fileSystemOptions); 175 if (dataTimeout != null) { 176 client.setDataTimeout(dataTimeout.intValue()); 177 } 178 179 final Integer socketTimeout = builder.getSoTimeout(fileSystemOptions); 180 if (socketTimeout != null) { 181 client.setSoTimeout(socketTimeout.intValue()); 182 } 183 184 final Boolean userDirIsRoot = builder.getUserDirIsRoot(fileSystemOptions); 185 if (workingDirectory != null && (userDirIsRoot == null || !userDirIsRoot.booleanValue())) { 186 if (!client.changeWorkingDirectory(workingDirectory)) { 187 throw new FileSystemException("vfs.provider.ftp/change-work-directory.error", 188 workingDirectory); 189 } 190 } 191 192 final Boolean passiveMode = builder.getPassiveMode(fileSystemOptions); 193 if (passiveMode != null && passiveMode.booleanValue()) { 194 client.enterLocalPassiveMode(); 195 } 196 197 setupOpenConnection(client, fileSystemOptions); 198 } catch (final IOException e) { 199 if (client.isConnected()) { 200 client.disconnect(); 201 } 202 throw e; 203 } 204 205 return client; 206 } catch (final Exception exc) { 207 throw new FileSystemException("vfs.provider.ftp/connect.error", exc, hostname); 208 } 209 } 210 211 protected abstract C createClient(FileSystemOptions fileSystemOptions) throws FileSystemException; 212 213 protected abstract void setupOpenConnection(C client, FileSystemOptions fileSystemOptions) throws IOException; 214 215 private void configureClient(final FileSystemOptions fileSystemOptions, final C client) { 216 final String key = builder.getEntryParser(fileSystemOptions); 217 if (key != null) { 218 final FTPClientConfig config = new FTPClientConfig(key); 219 220 final String serverLanguageCode = builder.getServerLanguageCode(fileSystemOptions); 221 if (serverLanguageCode != null) { 222 config.setServerLanguageCode(serverLanguageCode); 223 } 224 final String defaultDateFormat = builder.getDefaultDateFormat(fileSystemOptions); 225 if (defaultDateFormat != null) { 226 config.setDefaultDateFormatStr(defaultDateFormat); 227 } 228 final String recentDateFormat = builder.getRecentDateFormat(fileSystemOptions); 229 if (recentDateFormat != null) { 230 config.setRecentDateFormatStr(recentDateFormat); 231 } 232 final String serverTimeZoneId = builder.getServerTimeZoneId(fileSystemOptions); 233 if (serverTimeZoneId != null) { 234 config.setServerTimeZoneId(serverTimeZoneId); 235 } 236 final String[] shortMonthNames = builder.getShortMonthNames(fileSystemOptions); 237 if (shortMonthNames != null) { 238 final StringBuilder shortMonthNamesStr = new StringBuilder(BUFSZ); 239 for (final String shortMonthName : shortMonthNames) { 240 if (shortMonthNamesStr.length() > 0) { 241 shortMonthNamesStr.append("|"); 242 } 243 shortMonthNamesStr.append(shortMonthName); 244 } 245 config.setShortMonthNames(shortMonthNamesStr.toString()); 246 } 247 248 client.configure(config); 249 } 250 } 251 } 252}