1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.sftp;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.time.Duration;
22 import java.util.Properties;
23
24 import org.apache.commons.lang3.SystemUtils;
25 import org.apache.commons.lang3.time.DurationUtils;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.commons.vfs2.FileSystemException;
29 import org.apache.commons.vfs2.FileSystemOptions;
30
31 import com.jcraft.jsch.ConfigRepository;
32 import com.jcraft.jsch.JSch;
33 import com.jcraft.jsch.JSchException;
34 import com.jcraft.jsch.Logger;
35 import com.jcraft.jsch.OpenSSHConfig;
36 import com.jcraft.jsch.Proxy;
37 import com.jcraft.jsch.ProxyHTTP;
38 import com.jcraft.jsch.ProxySOCKS5;
39 import com.jcraft.jsch.Session;
40 import com.jcraft.jsch.UserInfo;
41
42
43
44
45 public final class SftpClientFactory {
46
47
48 private static class JSchLogger implements Logger {
49 @Override
50 public boolean isEnabled(final int level) {
51 switch (level) {
52 case FATAL:
53 return LOG.isFatalEnabled();
54 case ERROR:
55 return LOG.isErrorEnabled();
56 case WARN:
57 return LOG.isDebugEnabled();
58 case DEBUG:
59 return LOG.isDebugEnabled();
60 case INFO:
61 return LOG.isInfoEnabled();
62 default:
63 return LOG.isDebugEnabled();
64
65 }
66 }
67
68 @Override
69 public void log(final int level, final String msg) {
70 switch (level) {
71 case FATAL:
72 LOG.fatal(msg);
73 break;
74 case ERROR:
75 LOG.error(msg);
76 break;
77 case WARN:
78 LOG.warn(msg);
79 break;
80 case DEBUG:
81 LOG.debug(msg);
82 break;
83 case INFO:
84 LOG.info(msg);
85 break;
86 default:
87 LOG.debug(msg);
88 }
89 }
90 }
91 private static final String SSH_DIR_NAME = ".ssh";
92 private static final String OPENSSH_CONFIG_NAME = "config";
93
94 private static final Log LOG = LogFactory.getLog(SftpClientFactory.class);
95
96 static {
97 JSch.setLogger(new JSchLogger());
98 }
99
100 private static void addIdentities(final JSch jsch, final File sshDir, final IdentityProvider[] identities)
101 throws FileSystemException {
102 if (identities != null) {
103 for (final IdentityProvider info : identities) {
104 addIdentity(jsch, info);
105 }
106 } else {
107
108 final File privateKeyFile = new File(sshDir, "id_rsa");
109 if (privateKeyFile.isFile() && privateKeyFile.canRead()) {
110 addIdentity(jsch, new IdentityInfo(privateKeyFile));
111 }
112 }
113 }
114
115 private static void addIdentity(final JSch jsch, final IdentityProvider identity) throws FileSystemException {
116 try {
117 identity.addIdentity(jsch);
118 } catch (final JSchException e) {
119 throw new FileSystemException("vfs.provider.sftp/load-private-key.error", identity, e);
120 }
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134 public static Session createConnection(final String hostname, final int port, final char[] username,
135 final char[] password, final FileSystemOptions fileSystemOptions) throws FileSystemException {
136 final JSch jsch = new JSch();
137
138
139 final SftpFileSystemConfigBuilder builder = SftpFileSystemConfigBuilder.getInstance();
140 final File knownHostsFile = builder.getKnownHosts(fileSystemOptions);
141 final IdentityProvider[] identities = builder.getIdentityProvider(fileSystemOptions);
142 final IdentityRepositoryFactory repositoryFactory = builder.getIdentityRepositoryFactory(fileSystemOptions);
143 final ConfigRepository configRepository = builder.getConfigRepository(fileSystemOptions);
144 final boolean loadOpenSSHConfig = builder.isLoadOpenSSHConfig(fileSystemOptions);
145
146 final File sshDir = findSshDir();
147
148 setKnownHosts(jsch, sshDir, knownHostsFile);
149
150 if (repositoryFactory != null) {
151 jsch.setIdentityRepository(repositoryFactory.create(jsch));
152 }
153
154 addIdentities(jsch, sshDir, identities);
155 setConfigRepository(jsch, sshDir, configRepository, loadOpenSSHConfig);
156
157 final Session session;
158 try {
159 session = jsch.getSession(new String(username), hostname, port);
160 if (password != null) {
161 session.setPassword(new String(password));
162 }
163
164 final Duration sessionTimeout = builder.getSessionTimeout(fileSystemOptions);
165 if (sessionTimeout != null) {
166 session.setTimeout(DurationUtils.toMillisInt(sessionTimeout));
167 }
168
169 final UserInfo userInfo = builder.getUserInfo(fileSystemOptions);
170 if (userInfo != null) {
171 session.setUserInfo(userInfo);
172 }
173
174 final Properties config = new Properties();
175
176
177 final String strictHostKeyChecking = builder.getStrictHostKeyChecking(fileSystemOptions);
178 if (strictHostKeyChecking != null) {
179 config.setProperty("StrictHostKeyChecking", strictHostKeyChecking);
180 }
181
182 final String preferredAuthentications = builder.getPreferredAuthentications(fileSystemOptions);
183 if (preferredAuthentications != null) {
184 config.setProperty("PreferredAuthentications", preferredAuthentications);
185 }
186
187
188 final String compression = builder.getCompression(fileSystemOptions);
189 if (compression != null) {
190 config.setProperty("compression.s2c", compression);
191 config.setProperty("compression.c2s", compression);
192 }
193
194 final String keyExchangeAlgorithm = builder.getKeyExchangeAlgorithm(fileSystemOptions);
195 if (keyExchangeAlgorithm != null) {
196 config.setProperty("kex", keyExchangeAlgorithm);
197 }
198
199 final String proxyHost = builder.getProxyHost(fileSystemOptions);
200 if (proxyHost != null) {
201 final int proxyPort = builder.getProxyPort(fileSystemOptions);
202 final SftpFileSystemConfigBuilder.ProxyType proxyType = builder.getProxyType(fileSystemOptions);
203 final String proxyUser = builder.getProxyUser(fileSystemOptions);
204 final String proxyPassword = builder.getProxyPassword(fileSystemOptions);
205 Proxy proxy = null;
206 if (SftpFileSystemConfigBuilder.PROXY_HTTP.equals(proxyType)) {
207 proxy = createProxyHTTP(proxyHost, proxyPort);
208 ((ProxyHTTP)proxy).setUserPasswd(proxyUser, proxyPassword);
209 } else if (SftpFileSystemConfigBuilder.PROXY_SOCKS5.equals(proxyType)) {
210 proxy = createProxySOCKS5(proxyHost, proxyPort);
211 ((ProxySOCKS5)proxy).setUserPasswd(proxyUser, proxyPassword);
212 } else if (SftpFileSystemConfigBuilder.PROXY_STREAM.equals(proxyType)) {
213 proxy = createStreamProxy(proxyHost, proxyPort, fileSystemOptions, builder);
214 }
215
216 if (proxy != null) {
217 session.setProxy(proxy);
218 }
219 }
220
221
222 if (!config.isEmpty()) {
223 session.setConfig(config);
224 }
225 session.setDaemonThread(true);
226 session.connect();
227 } catch (final Exception exc) {
228 throw new FileSystemException("vfs.provider.sftp/connect.error", exc, hostname);
229 }
230
231 return session;
232 }
233
234 private static ProxyHTTP createProxyHTTP(final String proxyHost, final int proxyPort) {
235 return proxyPort == 0 ? new ProxyHTTP(proxyHost) : new ProxyHTTP(proxyHost, proxyPort);
236 }
237
238 private static ProxySOCKS5 createProxySOCKS5(final String proxyHost, final int proxyPort) {
239 return proxyPort == 0 ? new ProxySOCKS5(proxyHost) : new ProxySOCKS5(proxyHost, proxyPort);
240 }
241
242 private static Proxy createStreamProxy(final String proxyHost, final int proxyPort,
243 final FileSystemOptions fileSystemOptions, final SftpFileSystemConfigBuilder builder) {
244
245
246
247
248
249
250 final String proxyUser = builder.getProxyUser(fileSystemOptions);
251 final String proxyPassword = builder.getProxyPassword(fileSystemOptions);
252 final FileSystemOptions proxyOptions = builder.getProxyOptions(fileSystemOptions);
253
254 final String proxyCommand = builder.getProxyCommand(fileSystemOptions);
255
256
257 return new SftpStreamProxy(proxyCommand, proxyUser, proxyHost, proxyPort, proxyPassword, proxyOptions);
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 private static File findSshDir() {
281 final String sshDirPath;
282 sshDirPath = System.getProperty("vfs.sftp.sshdir");
283 if (sshDirPath != null) {
284 final File sshDir = new File(sshDirPath);
285 if (sshDir.exists()) {
286 return sshDir;
287 }
288 }
289
290 File sshDir = new File(System.getProperty("user.home"), SSH_DIR_NAME);
291 if (sshDir.exists()) {
292 return sshDir;
293 }
294
295 if (SystemUtils.IS_OS_WINDOWS) {
296
297 final String userName = System.getProperty("user.name");
298 sshDir = new File("C:\\cygwin\\home\\" + userName + "\\" + SSH_DIR_NAME);
299 if (sshDir.exists()) {
300 return sshDir;
301 }
302 }
303 return new File("");
304 }
305
306 private static void setConfigRepository(final JSch jsch, final File sshDir, final ConfigRepository configRepository, final boolean loadOpenSSHConfig) throws FileSystemException {
307 if (configRepository != null) {
308 jsch.setConfigRepository(configRepository);
309 } else if (loadOpenSSHConfig) {
310 try {
311
312 final ConfigRepository openSSHConfig = OpenSSHConfig.parseFile(new File(sshDir, OPENSSH_CONFIG_NAME).getAbsolutePath());
313 jsch.setConfigRepository(openSSHConfig);
314 } catch (final IOException e) {
315 throw new FileSystemException("vfs.provider.sftp/load-openssh-config.error", e);
316 }
317 }
318 }
319
320 private static void setKnownHosts(final JSch jsch, final File sshDir, File knownHostsFile)
321 throws FileSystemException {
322 try {
323 if (knownHostsFile != null) {
324 jsch.setKnownHosts(knownHostsFile.getAbsolutePath());
325 } else {
326
327 knownHostsFile = new File(sshDir, "known_hosts");
328 if (knownHostsFile.isFile() && knownHostsFile.canRead()) {
329 jsch.setKnownHosts(knownHostsFile.getAbsolutePath());
330 }
331 }
332 } catch (final JSchException e) {
333 throw new FileSystemException("vfs.provider.sftp/known-hosts.error", knownHostsFile.getAbsolutePath(), e);
334 }
335
336 }
337
338 private SftpClientFactory() {
339 }
340 }