/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.vfs2.provider.sftp;

import java.io.File;
import java.io.IOException;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.util.Os;

import com.jcraft.jsch.ConfigRepository;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Logger;
import com.jcraft.jsch.OpenSSHConfig;
import com.jcraft.jsch.Proxy;
import com.jcraft.jsch.ProxyHTTP;
import com.jcraft.jsch.ProxySOCKS5;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;

Create a JSch Session instance.
/** * Create a JSch Session instance. */
public final class SftpClientFactory { private static final String SSH_DIR_NAME = ".ssh"; private static final String OPENSSH_CONFIG_NAME = "config"; private static final Log LOG = LogFactory.getLog(SftpClientFactory.class); static { JSch.setLogger(new JSchLogger()); } private SftpClientFactory() { }
Creates a new connection to the server.
Params:
  • hostname – The name of the host to connect to.
  • port – The port to use.
  • username – The user's id.
  • password – The user's password.
  • fileSystemOptions – The FileSystem options.
Throws:
Returns:A Session, never null.
/** * Creates a new connection to the server. * * @param hostname The name of the host to connect to. * @param port The port to use. * @param username The user's id. * @param password The user's password. * @param fileSystemOptions The FileSystem options. * @return A Session, never null. * @throws FileSystemException if an error occurs. */
public static Session createConnection(final String hostname, final int port, final char[] username, final char[] password, final FileSystemOptions fileSystemOptions) throws FileSystemException { final JSch jsch = new JSch(); File sshDir = null; // new style - user passed final SftpFileSystemConfigBuilder builder = SftpFileSystemConfigBuilder.getInstance(); final File knownHostsFile = builder.getKnownHosts(fileSystemOptions); final IdentityProvider[] identities = builder.getIdentityProvider(fileSystemOptions); final IdentityRepositoryFactory repositoryFactory = builder.getIdentityRepositoryFactory(fileSystemOptions); final ConfigRepository configRepository = builder.getConfigRepository(fileSystemOptions); final boolean loadOpenSSHConfig = builder.isLoadOpenSSHConfig(fileSystemOptions); sshDir = findSshDir(); setKnownHosts(jsch, sshDir, knownHostsFile); if (repositoryFactory != null) { jsch.setIdentityRepository(repositoryFactory.create(jsch)); } addIdentities(jsch, sshDir, identities); setConfigRepository(jsch, sshDir, configRepository, loadOpenSSHConfig); Session session; try { session = jsch.getSession(new String(username), hostname, port); if (password != null) { session.setPassword(new String(password)); } final Integer sessionTimeout = builder.getSessionTimeoutMillis(fileSystemOptions); if (sessionTimeout != null) { session.setTimeout(sessionTimeout.intValue()); } final UserInfo userInfo = builder.getUserInfo(fileSystemOptions); if (userInfo != null) { session.setUserInfo(userInfo); } final Properties config = new Properties(); // set StrictHostKeyChecking property final String strictHostKeyChecking = builder.getStrictHostKeyChecking(fileSystemOptions); if (strictHostKeyChecking != null) { config.setProperty("StrictHostKeyChecking", strictHostKeyChecking); } // set PreferredAuthentications property final String preferredAuthentications = builder.getPreferredAuthentications(fileSystemOptions); if (preferredAuthentications != null) { config.setProperty("PreferredAuthentications", preferredAuthentications); } // set compression property final String compression = builder.getCompression(fileSystemOptions); if (compression != null) { config.setProperty("compression.s2c", compression); config.setProperty("compression.c2s", compression); } final String keyExchangeAlgorithm = builder.getKeyExchangeAlgorithm(fileSystemOptions); if (keyExchangeAlgorithm != null) { config.setProperty("kex", keyExchangeAlgorithm); } final String proxyHost = builder.getProxyHost(fileSystemOptions); if (proxyHost != null) { final int proxyPort = builder.getProxyPort(fileSystemOptions); final SftpFileSystemConfigBuilder.ProxyType proxyType = builder.getProxyType(fileSystemOptions); final String proxyUser = builder.getProxyUser(fileSystemOptions); final String proxyPassword = builder.getProxyPassword(fileSystemOptions); Proxy proxy = null; if (SftpFileSystemConfigBuilder.PROXY_HTTP.equals(proxyType)) { proxy = createProxyHTTP(proxyHost, proxyPort); ((ProxyHTTP)proxy).setUserPasswd(proxyUser, proxyPassword); } else if (SftpFileSystemConfigBuilder.PROXY_SOCKS5.equals(proxyType)) { proxy = createProxySOCKS5(proxyHost, proxyPort); ((ProxySOCKS5)proxy).setUserPasswd(proxyUser, proxyPassword); } else if (SftpFileSystemConfigBuilder.PROXY_STREAM.equals(proxyType)) { proxy = createStreamProxy(proxyHost, proxyPort, fileSystemOptions, builder); } if (proxy != null) { session.setProxy(proxy); } } // set properties for the session if (config.size() > 0) { session.setConfig(config); } session.setDaemonThread(true); session.connect(); } catch (final Exception exc) { throw new FileSystemException("vfs.provider.sftp/connect.error", exc, hostname); } return session; } private static void addIdentities(final JSch jsch, final File sshDir, final IdentityProvider[] identities) throws FileSystemException { if (identities != null) { for (final IdentityProvider info : identities) { addIdentity(jsch, info); } } else { // Load the private key (rsa-key only) final File privateKeyFile = new File(sshDir, "id_rsa"); if (privateKeyFile.isFile() && privateKeyFile.canRead()) { addIdentity(jsch, new IdentityInfo(privateKeyFile)); } } } private static void setConfigRepository(final JSch jsch, final File sshDir, final ConfigRepository configRepository, final boolean loadOpenSSHConfig) throws FileSystemException { if (configRepository != null) { jsch.setConfigRepository(configRepository); } else if (loadOpenSSHConfig) { try { // loading openssh config (~/.ssh/config) final ConfigRepository openSSHConfig = OpenSSHConfig.parseFile(new File(sshDir, OPENSSH_CONFIG_NAME).getAbsolutePath()); jsch.setConfigRepository(openSSHConfig); } catch (final IOException e) { throw new FileSystemException("vfs.provider.sftp/load-openssh-config.error", e); } } } private static void addIdentity(final JSch jsch, final IdentityProvider identity) throws FileSystemException { try { identity.addIdentity(jsch); } catch (final JSchException e) { throw new FileSystemException("vfs.provider.sftp/load-private-key.error", identity, e); } } private static void setKnownHosts(final JSch jsch, final File sshDir, File knownHostsFile) throws FileSystemException { try { if (knownHostsFile != null) { jsch.setKnownHosts(knownHostsFile.getAbsolutePath()); } else { // Load the known hosts file knownHostsFile = new File(sshDir, "known_hosts"); if (knownHostsFile.isFile() && knownHostsFile.canRead()) { jsch.setKnownHosts(knownHostsFile.getAbsolutePath()); } } } catch (final JSchException e) { throw new FileSystemException("vfs.provider.sftp/known-hosts.error", knownHostsFile.getAbsolutePath(), e); } } private static Proxy createStreamProxy(final String proxyHost, final int proxyPort, final FileSystemOptions fileSystemOptions, final SftpFileSystemConfigBuilder builder) { Proxy proxy; // Use a stream proxy, i.e. it will use a remote host as a proxy // and run a command (e.g. netcat) that forwards input/output // to the target host. // Here we get the settings for connecting to the proxy: // user, password, options and a command final String proxyUser = builder.getProxyUser(fileSystemOptions); final String proxyPassword = builder.getProxyPassword(fileSystemOptions); final FileSystemOptions proxyOptions = builder.getProxyOptions(fileSystemOptions); final String proxyCommand = builder.getProxyCommand(fileSystemOptions); // Create the stream proxy proxy = new SftpStreamProxy(proxyCommand, proxyUser, proxyHost, proxyPort, proxyPassword, proxyOptions); return proxy; } private static ProxySOCKS5 createProxySOCKS5(final String proxyHost, final int proxyPort) { return proxyPort == 0 ? new ProxySOCKS5(proxyHost) : new ProxySOCKS5(proxyHost, proxyPort); } private static ProxyHTTP createProxyHTTP(final String proxyHost, final int proxyPort) { return proxyPort == 0 ? new ProxyHTTP(proxyHost) : new ProxyHTTP(proxyHost, proxyPort); }
Finds the .ssh directory.

The lookup order is:

  1. The system property vfs.sftp.sshdir (the override mechanism)
  2. user.home/.ssh
  3. On Windows only: C:\cygwin\home[user.name]\.ssh
  4. The current directory, as a last resort.

Windows Notes

The default installation directory for Cygwin is C:\cygwin. On my set up (Gary here), I have Cygwin in C:\bin\cygwin, not the default. Also, my .ssh directory was created in the user.home directory.

Returns:The .ssh directory
/** * Finds the {@code .ssh} directory. * <p> * The lookup order is: * </p> * <ol> * <li>The system property {@code vfs.sftp.sshdir} (the override mechanism)</li> * <li>{@code user.home}/.ssh</li> * <li>On Windows only: {@code C:\cygwin\home[user.name]\.ssh}</li> * <li>The current directory, as a last resort.</li> * </ol> * * <h2>Windows Notes</h2> * <p> * The default installation directory for Cygwin is {@code C:\cygwin}. On my set up (Gary here), I have Cygwin in * {@code C:\bin\cygwin}, not the default. Also, my .ssh directory was created in the {@code user.home} directory. * </p> * * @return The {@code .ssh} directory */
private static File findSshDir() { String sshDirPath; sshDirPath = System.getProperty("vfs.sftp.sshdir"); if (sshDirPath != null) { final File sshDir = new File(sshDirPath); if (sshDir.exists()) { return sshDir; } } File sshDir = new File(System.getProperty("user.home"), SSH_DIR_NAME); if (sshDir.exists()) { return sshDir; } if (Os.isFamily(Os.OS_FAMILY_WINDOWS)) { // TODO - this may not be true final String userName = System.getProperty("user.name"); sshDir = new File("C:\\cygwin\\home\\" + userName + "\\" + SSH_DIR_NAME); if (sshDir.exists()) { return sshDir; } } return new File(""); }
Interface JSchLogger with JCL.
/** Interface JSchLogger with JCL. */
private static class JSchLogger implements Logger { @Override public boolean isEnabled(final int level) { switch (level) { case FATAL: return LOG.isFatalEnabled(); case ERROR: return LOG.isErrorEnabled(); case WARN: return LOG.isDebugEnabled(); case DEBUG: return LOG.isDebugEnabled(); case INFO: return LOG.isInfoEnabled(); default: return LOG.isDebugEnabled(); } } @Override public void log(final int level, final String msg) { switch (level) { case FATAL: LOG.fatal(msg); break; case ERROR: LOG.error(msg); break; case WARN: LOG.warn(msg); break; case DEBUG: LOG.debug(msg); break; case INFO: LOG.info(msg); break; default: LOG.debug(msg); } } } }