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 */
017 package org.apache.commons.vfs2.provider.sftp;
018
019 import java.io.IOException;
020 import java.util.Collection;
021
022 import org.apache.commons.vfs2.Capability;
023 import org.apache.commons.vfs2.FileObject;
024 import org.apache.commons.vfs2.FileSystem;
025 import org.apache.commons.vfs2.FileSystemException;
026 import org.apache.commons.vfs2.FileSystemOptions;
027 import org.apache.commons.vfs2.UserAuthenticationData;
028 import org.apache.commons.vfs2.provider.AbstractFileName;
029 import org.apache.commons.vfs2.provider.AbstractFileSystem;
030 import org.apache.commons.vfs2.provider.GenericFileName;
031 import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
032
033 import com.jcraft.jsch.ChannelSftp;
034 import com.jcraft.jsch.JSchException;
035 import com.jcraft.jsch.Session;
036 import com.jcraft.jsch.SftpException;
037
038 /**
039 * Represents the files on an SFTP server.
040 *
041 * @author <a href="http://commons.apache.org/vfs/team-list.html">Commons VFS team</a>
042 */
043 public class SftpFileSystem
044 extends AbstractFileSystem
045 implements FileSystem
046 {
047
048 private Session session;
049 // private final JSch jSch;
050 private ChannelSftp idleChannel;
051
052 protected SftpFileSystem(final GenericFileName rootName,
053 final Session session,
054 final FileSystemOptions fileSystemOptions)
055 {
056 super(rootName, null, fileSystemOptions);
057 this.session = session;
058 }
059
060 @Override
061 protected void doCloseCommunicationLink()
062 {
063 if (idleChannel != null)
064 {
065 idleChannel.disconnect();
066 idleChannel = null;
067 }
068
069 if (session != null)
070 {
071 session.disconnect();
072 session = null;
073 }
074 }
075
076 /**
077 * Returns an SFTP channel to the server.
078 */
079 protected ChannelSftp getChannel() throws IOException
080 {
081 if (this.session == null || !this.session.isConnected())
082 {
083 doCloseCommunicationLink();
084
085 // channel closed. e.g. by freeUnusedResources, but now we need it again
086 Session session;
087 UserAuthenticationData authData = null;
088 try
089 {
090 final GenericFileName rootName = (GenericFileName) getRootName();
091
092 authData = UserAuthenticatorUtils.authenticate(getFileSystemOptions(),
093 SftpFileProvider.AUTHENTICATOR_TYPES);
094
095 session = SftpClientFactory.createConnection(
096 rootName.getHostName(),
097 rootName.getPort(),
098 UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME,
099 UserAuthenticatorUtils.toChar(rootName.getUserName())),
100 UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD,
101 UserAuthenticatorUtils.toChar(rootName.getPassword())),
102 getFileSystemOptions());
103 }
104 catch (final Exception e)
105 {
106 throw new FileSystemException("vfs.provider.sftp/connect.error",
107 getRootName(),
108 e);
109 }
110 finally
111 {
112 UserAuthenticatorUtils.cleanup(authData);
113 }
114
115 this.session = session;
116 }
117
118 try
119 {
120 // Use the pooled channel, or create a new one
121 final ChannelSftp channel;
122 if (idleChannel != null)
123 {
124 channel = idleChannel;
125 idleChannel = null;
126 }
127 else
128 {
129 channel = (ChannelSftp) session.openChannel("sftp");
130 channel.connect();
131
132 Boolean userDirIsRoot =
133 SftpFileSystemConfigBuilder.getInstance().getUserDirIsRoot(getFileSystemOptions());
134 String workingDirectory = getRootName().getPath();
135 if (workingDirectory != null && (userDirIsRoot == null || !userDirIsRoot.booleanValue()))
136 {
137 try
138 {
139 channel.cd(workingDirectory);
140 }
141 catch (SftpException e)
142 {
143 throw new FileSystemException("vfs.provider.sftp/change-work-directory.error",
144 workingDirectory);
145 }
146 }
147 }
148
149 return channel;
150 }
151 catch (final JSchException e)
152 {
153 throw new FileSystemException("vfs.provider.sftp/connect.error",
154 getRootName(),
155 e);
156 }
157 }
158
159 /**
160 * Returns a channel to the pool.
161 */
162 protected void putChannel(final ChannelSftp channel)
163 {
164 if (idleChannel == null)
165 {
166 // put back the channel only if it is still connected
167 if (channel.isConnected() && !channel.isClosed())
168 {
169 idleChannel = channel;
170 }
171 }
172 else
173 {
174 channel.disconnect();
175 }
176 }
177
178 /**
179 * Adds the capabilities of this file system.
180 */
181 @Override
182 protected void addCapabilities(final Collection<Capability> caps)
183 {
184 caps.addAll(SftpFileProvider.capabilities);
185 }
186
187 /**
188 * Creates a file object. This method is called only if the requested
189 * file is not cached.
190 */
191 @Override
192 protected FileObject createFile(final AbstractFileName name)
193 throws FileSystemException
194 {
195 return new SftpFileObject(name, this);
196 }
197
198 /**
199 * last mod time is only a int and in seconds, thus can be off by 999.
200 *
201 * @return 1000
202 */
203 @Override
204 public double getLastModTimeAccuracy()
205 {
206 return 1000L;
207 }
208 }