blob: 54488b9e394353ba6e1ec0c28f76b4eff4ddd3b7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - Initial API and implementation
* Patrick Tasse - [462418] use stored password on non-preferred password based authentication
* Martin Oberhuber - [468889] Support Eclipse older than Mars
*******************************************************************************/
package org.eclipse.remote.internal.jsch.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jsch.core.IJSchService;
import org.eclipse.osgi.util.NLS;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteConnectionChangeListener;
import org.eclipse.remote.core.IRemoteConnectionControlService;
import org.eclipse.remote.core.IRemoteConnectionHostService;
import org.eclipse.remote.core.IRemoteConnectionPropertyService;
import org.eclipse.remote.core.IRemoteConnectionWorkingCopy;
import org.eclipse.remote.core.IRemotePortForwardingService;
import org.eclipse.remote.core.IRemoteProcessBuilder;
import org.eclipse.remote.core.IRemoteProcessService;
import org.eclipse.remote.core.IUserAuthenticatorService;
import org.eclipse.remote.core.RemoteConnectionChangeEvent;
import org.eclipse.remote.core.RemoteServicesUtils;
import org.eclipse.remote.core.exception.AddressInUseException;
import org.eclipse.remote.core.exception.RemoteConnectionException;
import org.eclipse.remote.core.exception.UnableToForwardPortException;
import org.eclipse.remote.internal.jsch.core.commands.ExecCommand;
import org.eclipse.remote.internal.jsch.core.messages.Messages;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* @since 5.0
*/
public class JSchConnection implements IRemoteConnectionControlService, IRemoteConnectionPropertyService,
IRemotePortForwardingService, IRemoteProcessService, IRemoteConnectionHostService, IRemoteConnectionChangeListener {
// Connection Type ID
public static final String JSCH_ID = "org.eclipse.remote.JSch"; //$NON-NLS-1$
// Attributes
public static final String ADDRESS_ATTR = "JSCH_ADDRESS_ATTR"; //$NON-NLS-1$
public static final String USERNAME_ATTR = "JSCH_USERNAME_ATTR"; //$NON-NLS-1$
public static final String PASSWORD_ATTR = "JSCH_PASSWORD_ATTR"; //$NON-NLS-1$
public static final String PORT_ATTR = "JSCH_PORT_ATTR"; //$NON-NLS-1$
public static final String PROXYCONNECTION_ATTR = "JSCH_PROXYCONNECTION_ATTR"; //$NON-NLS-1$
public static final String PROXYCOMMAND_ATTR = "JSCH_PROXYCOMMAND_ATTR"; //$NON-NLS-1$
public static final String IS_PASSWORD_ATTR = "JSCH_IS_PASSWORD_ATTR"; //$NON-NLS-1$
public static final String PASSPHRASE_ATTR = "JSCH_PASSPHRASE_ATTR"; //$NON-NLS-1$
public static final String TIMEOUT_ATTR = "JSCH_TIMEOUT_ATTR"; //$NON-NLS-1$
public static final String USE_LOGIN_SHELL_ATTR = "JSCH_USE_LOGIN_SHELL_ATTR"; //$NON-NLS-1$
public static final String LOGIN_SHELL_COMMAND_ATTR = "JSCH_LOGIN_SHELL_COMMAND_ATTR"; //$NON-NLS-1$
public static final int DEFAULT_PORT = 22;
public static final int DEFAULT_TIMEOUT = 0;
public static final boolean DEFAULT_IS_PASSWORD = false;
public static final boolean DEFAULT_USE_LOGIN_SHELL = true;
public static final String DEFAULT_LOGIN_SHELL_COMMAND = "/bin/bash -l -c '{0}'"; //$NON-NLS-1$
public static final String DEFAULT_ENCODING = "UTF-8"; //$NON-NLS-1$
public static final String EMPTY_STRING = ""; //$NON-NLS-1$
private String fWorkingDir;
private final IRemoteConnection fRemoteConnection;
private final IJSchService fJSchService;
private final Map<String, String> fEnv = new HashMap<>();
private final Map<String, String> fProperties = new HashMap<>();
private final List<Session> fSessions = new ArrayList<>();
private ChannelSftp fSftpCommandChannel;
private boolean isFullySetup; // including sftp channel and environment
private static final Map<IRemoteConnection, JSchConnection> connectionMap = new HashMap<>();
public JSchConnection(IRemoteConnection connection) {
fRemoteConnection = connection;
fJSchService = Activator.getDefault().getService();
connection.addConnectionChangeListener(this);
}
@Override
public void connectionChanged(RemoteConnectionChangeEvent event) {
if (event.getType() == RemoteConnectionChangeEvent.CONNECTION_REMOVED) {
synchronized (connectionMap) {
connectionMap.remove(event.getConnection());
}
}
}
@Override
public IRemoteConnection getRemoteConnection() {
return fRemoteConnection;
}
public static class Factory implements IRemoteConnection.Service.Factory {
@Override
@SuppressWarnings("unchecked")
public <T extends IRemoteConnection.Service> T getService(IRemoteConnection connection, Class<T> service) {
// This little trick creates an instance of this class for a connection
// then for each interface it implements, it returns the same object.
// This works because the connection caches the service so only one gets created.
// As a side effect, it makes this class a service too which can be used
// by the this plug-in
if (JSchConnection.class.equals(service)) {
synchronized (connectionMap) {
JSchConnection jschConnection = connectionMap.get(connection);
if (jschConnection == null) {
jschConnection = new JSchConnection(connection);
connectionMap.put(connection, jschConnection);
}
return (T) jschConnection;
}
} else if (IRemoteConnectionControlService.class.equals(service)
|| IRemoteConnectionPropertyService.class.equals(service) || IRemotePortForwardingService.class.equals(service)
|| IRemoteProcessService.class.equals(service) || IRemoteConnectionHostService.class.equals(service)) {
return (T) connection.getService(JSchConnection.class);
} else {
return null;
}
}
}
private boolean checkConfiguration(Session session, IProgressMonitor monitor) throws RemoteConnectionException {
SubMonitor subMon = SubMonitor.convert(monitor, 10);
ChannelSftp sftp;
try {
/*
* First, check if sftp is supported at all. This is required for EFS, so throw exception if not supported.
*/
sftp = openSftpChannel(session);
} catch (RemoteConnectionException e) {
throw new RemoteConnectionException(Messages.JSchConnection_Remote_host_does_not_support_sftp);
}
/*
* While sftp channel is open, try opening an exec channel. If it doesn't succeed, then MaxSession is < 2 so we need at
* least one additional session.
*/
try {
loadEnv(subMon.newChild(10));
} catch (RemoteConnectionException e) {
if (e.getMessage().contains("channel is not opened")) { //$NON-NLS-1$
return false;
}
} finally {
if (sftp != null) {
sftp.disconnect();
}
}
return true;
}
/**
* @throws RemoteConnectionException
*/
private void checkIsConfigured() throws RemoteConnectionException {
if (fRemoteConnection.getAttribute(ADDRESS_ATTR) == null) {
throw new RemoteConnectionException(Messages.JSchConnection_remote_address_must_be_set);
}
if (fRemoteConnection.getAttribute(USERNAME_ATTR) == null) {
throw new RemoteConnectionException(Messages.JSchConnection_username_must_be_set);
}
}
private synchronized void cleanup() {
if (fSftpCommandChannel != null) {
if (fSftpCommandChannel.isConnected()) {
fSftpCommandChannel.disconnect();
}
fSftpCommandChannel = null;
}
for (Session session : fSessions) {
if (session.isConnected()) {
session.disconnect();
}
}
fSessions.clear();
}
@Override
public synchronized void close() {
cleanup();
fRemoteConnection.fireConnectionChangeEvent(RemoteConnectionChangeEvent.CONNECTION_CLOSED);
}
/**
* Execute the command and return the result as a string.
*
* @param cmd
* command to execute
* @param monitor
* progress monitor
* @return result of command
* @throws RemoteConnectionException
*/
private String executeCommand(String cmd, IProgressMonitor monitor) throws RemoteConnectionException {
ExecCommand exec = new ExecCommand(this);
monitor.subTask(NLS.bind(Messages.JSchConnection_Executing_command, cmd));
return exec.setCommand(cmd).getResult(monitor).trim();
}
@Override
public void forwardLocalPort(int localPort, String fwdAddress, int fwdPort) throws RemoteConnectionException {
if (!isOpen()) {
throw new RemoteConnectionException(Messages.JSchConnection_connectionNotOpen);
}
try {
fSessions.get(0).setPortForwardingL(localPort, fwdAddress, fwdPort);
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
@Override
public int forwardLocalPort(String fwdAddress, int fwdPort, IProgressMonitor monitor) throws RemoteConnectionException {
if (!isOpen()) {
throw new RemoteConnectionException(Messages.JSchConnection_connectionNotOpen);
}
SubMonitor progress = SubMonitor.convert(monitor, 10);
progress.beginTask(Messages.JSchConnection_forwarding, 10);
/*
* Start with a different port number, in case we're doing this all on localhost.
*/
int localPort = fwdPort + 1;
/*
* Try to find a free port on the remote machine. This take a while, so allow it to be canceled. If we've tried all
* ports (which could take a very long while) then bail out.
*/
while (!progress.isCanceled()) {
try {
forwardLocalPort(localPort, fwdAddress, fwdPort);
} catch (AddressInUseException e) {
if (++localPort == fwdPort) {
throw new UnableToForwardPortException(Messages.JSchConnection_remotePort);
}
progress.worked(1);
}
return localPort;
}
return -1;
}
@Override
public void forwardRemotePort(int remotePort, String fwdAddress, int fwdPort) throws RemoteConnectionException {
if (!isOpen()) {
throw new RemoteConnectionException(Messages.JSchConnection_connectionNotOpen);
}
try {
fSessions.get(0).setPortForwardingR(remotePort, fwdAddress, fwdPort);
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
@Override
public int forwardRemotePort(String fwdAddress, int fwdPort, IProgressMonitor monitor) throws RemoteConnectionException {
if (!isOpen()) {
throw new RemoteConnectionException(Messages.JSchConnection_connectionNotOpen);
}
SubMonitor progress = SubMonitor.convert(monitor, 10);
progress.beginTask(Messages.JSchConnection_forwarding, 10);
/*
* Start with a different port number, in case we're doing this all on localhost.
*/
int remotePort = fwdPort + 1;
/*
* Try to find a free port on the remote machine. This take a while, so allow it to be canceled. If we've tried all
* ports (which could take a very long while) then bail out.
*/
while (!progress.isCanceled()) {
try {
forwardRemotePort(remotePort, fwdAddress, fwdPort);
return remotePort;
} catch (AddressInUseException e) {
if (++remotePort == fwdPort) {
throw new UnableToForwardPortException(Messages.JSchConnection_remotePort);
}
progress.worked(1);
}
}
return -1;
}
@Override
public String getHostname() {
return fRemoteConnection.getAttribute(ADDRESS_ATTR);
}
/**
* Get the result of executing a pwd command.
*
* @return current working directory
*/
private String getCwd(IProgressMonitor monitor) {
SubMonitor subMon = SubMonitor.convert(monitor, 10);
try {
return executeCommand("pwd", subMon.newChild(10)); //$NON-NLS-1$
} catch (RemoteConnectionException e) {
// Ignore
}
return null;
}
@Override
public Map<String, String> getEnv() {
return Collections.unmodifiableMap(fEnv);
}
@Override
public String getEnv(String name) {
return getEnv().get(name);
}
/**
* Open an exec channel to the remote host.
*
* @return exec channel or null if the progress monitor was cancelled
*
* @throws RemoteConnectionException
* if a channel could not be opened
*/
public ChannelExec getExecChannel() throws RemoteConnectionException {
try {
return (ChannelExec) fSessions.get(0).openChannel("exec"); //$NON-NLS-1$
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
/**
* Open a shell channel to the remote host.
*
* @return shell channel or null if the progress monitor was cancelled
*
* @throws RemoteConnectionException
* if a channel could not be opened
*/
public ChannelShell getShellChannel() throws RemoteConnectionException {
try {
return (ChannelShell) fSessions.get(0).openChannel("shell"); //$NON-NLS-1$
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
public String getPassphrase() {
return fRemoteConnection.getSecureAttribute(PASSPHRASE_ATTR);
}
public String getPassword() {
return fRemoteConnection.getSecureAttribute(PASSWORD_ATTR);
}
@Override
public int getPort() {
String portStr = fRemoteConnection.getAttribute(PORT_ATTR);
return !portStr.isEmpty() ? Integer.parseInt(portStr) : DEFAULT_PORT;
}
@Override
public IRemoteProcessBuilder getProcessBuilder(List<String> command) {
if (!isOpen()) {
return null;
}
return new JSchProcessBuilder(getRemoteConnection(), command);
}
@Override
public IRemoteProcessBuilder getProcessBuilder(String... command) {
if (!isOpen()) {
return null;
}
return new JSchProcessBuilder(getRemoteConnection(), command);
}
@Override
public String getProperty(String key) {
return fProperties.get(key);
}
/**
* Get the login shell command if useLoginShell is true
*
* @return login shell command
*/
public String getLoginShellCommand() {
String loginShell = fRemoteConnection.getAttribute(LOGIN_SHELL_COMMAND_ATTR);
return loginShell.isEmpty() ? DEFAULT_LOGIN_SHELL_COMMAND : loginShell;
}
/**
* Gets the proxy command. For no proxy command null is returned.
*
* @return proxy command
*/
public String getProxyCommand() {
return fRemoteConnection.getAttribute(PROXYCOMMAND_ATTR);
}
/**
* Gets the proxy connection. If no proxy connection is used it returns null.
*
* @return proxy connection
*/
public JSchConnection getProxyConnection() {
String proxyConnectionName = getProxyConnectionName();
if (proxyConnectionName.isEmpty()) {
return null;
}
return fRemoteConnection.getConnectionType().getConnection(proxyConnectionName).getService(JSchConnection.class);
}
/**
* Gets the proxy connection name
*
* @return proxy connection name. If no proxy is used returns null.
*/
public String getProxyConnectionName() {
return fRemoteConnection.getAttribute(PROXYCONNECTION_ATTR);
}
/**
* Open an sftp command channel to the remote host. This channel is for commands that do not require any
* state being preserved and should not be closed. Long running commands (such as get/put) should use a separate channel
* obtained via {#link #newSftpChannel()}.
*
* Always use the second session if available.
*
* @return sftp channel
* @throws RemoteConnectionException
* if a channel could not be opened
*/
public ChannelSftp getSftpCommandChannel() throws RemoteConnectionException {
if (fSftpCommandChannel == null || fSftpCommandChannel.isClosed()) {
fSftpCommandChannel = newSftpChannel();
}
return fSftpCommandChannel;
}
/**
* Open a channel for long running commands. This channel should be closed when the command is completed.
*
* @return sftp channel
* @throws RemoteConnectionException
* if a channel could not be opened
*/
public ChannelSftp newSftpChannel() throws RemoteConnectionException {
Session session = fSessions.get(0);
if (fSessions.size() > 1) {
session = fSessions.get(1);
}
ChannelSftp channel = openSftpChannel(session);
if (channel == null) {
throw new RemoteConnectionException(Messages.JSchConnection_Unable_to_open_sftp_channel);
}
return channel;
}
public Channel getStreamForwarder(String host, int port) throws RemoteConnectionException {
try {
Channel channel = fSessions.get(0).getStreamForwarder(host, port);
channel.connect();
return channel;
} catch (JSchException e) {
throw new RemoteConnectionException(e);
}
}
@Override
public int getTimeout() {
String str = fRemoteConnection.getAttribute(TIMEOUT_ATTR);
return !str.isEmpty() ? Integer.parseInt(str) : DEFAULT_TIMEOUT;
}
@Override
public String getUsername() {
return fRemoteConnection.getAttribute(USERNAME_ATTR);
}
@Override
public String getWorkingDirectory() {
if (!isOpen()) {
return "/"; //$NON-NLS-1$
}
if (fWorkingDir == null) {
return "/"; //$NON-NLS-1$
}
return fWorkingDir;
}
/**
* Test if the connection has a valid open session. Doesn't check whether the connection is fully setup.
*
* @return true if a valid session is available.
*/
public boolean hasOpenSession() {
boolean hasOpenSession = fSessions.size() > 0;
if (hasOpenSession) {
for (Session session : fSessions) {
hasOpenSession &= session.isConnected();
}
}
if (!hasOpenSession) {
cleanup(); // Cleanup if session is closed
}
return hasOpenSession;
}
@Override
public boolean isOpen() {
return hasOpenSession() && isFullySetup;
}
public boolean usePassword() {
String str = fRemoteConnection.getAttribute(IS_PASSWORD_ATTR);
return !str.isEmpty() ? Boolean.parseBoolean(str) : DEFAULT_IS_PASSWORD;
}
private void loadEnv(IProgressMonitor monitor) throws RemoteConnectionException {
SubMonitor subMon = SubMonitor.convert(monitor, 10);
String env = executeCommand("printenv", subMon.newChild(10)); //$NON-NLS-1$
String[] vars = env.split("\n"); //$NON-NLS-1$
for (String var : vars) {
String[] kv = var.split("="); //$NON-NLS-1$
if (kv.length == 2) {
fEnv.put(kv[0], kv[1]);
}
}
}
/**
*
* Load the following hard-coded properties at runtime:
*
* <dl>
* <dt>file.separator
* <dd>File separator character of the (remote) connection. Hardcoded "/" (forward slash).
* <dt>path.separator
* <dd>Path separator character of the (remote) connection. Hardcoded ":" (colon).
* <dt>line.separator
* <dd>Line separator character of the (remote) connection. Hardcoded "\n" (new-line).
* <dt>user.home
* <dd>User home directory on the (remote) connection.
* <dt>os.name
* <dd>Operating system name of the (remote) connection. For example, given results from the "uname" command:
* <ul>
* <li>Linux</li>
* <li>AIX</li>
* <li>Mac OS X - if results equal "Darwin" then results from "sw_vers -productName"</li>
* <li>everything else - results from "uname" command</li>
* </ul>
* <dt>os.version
* <dd>Operating system version of the (remote) connection. For example:
* <ul>
* <li>For Linux - results from "uname -r" such as "2.6.32-279.2.1.el6.x86_64"</li>
* <li>For AIX - results from "oslevel" such as "7.1.0.0"</li>
* <li>For Mac OS X - results from "sw_vers -productVersion" such as "10.8.3"</li>
* <li>For everything else - "unknown"</li>
* </ul>
* <dt>os.arch
* <dd>Machine architecture of the (remote) connection. For example:
* <ul>
* <li>For Linux - results from "uname -m" such as "x86_64"</li>
* <li>For AIX - if results from "uname -p" equals "powerpc"
* <ul style="list-style: none;">
* <li>then if "prtconf -k" contains "64-bit" then "ppc64" else "ppc"</li>
* <li>else the result from "uname -p"</li>
* </ul>
* </li>
* <li>For Mac OS X - if results from "uname -m" equals "i386"
* <ul style="list-style: none;">
* <li>then if results from "sysctl -n hw.optional.x86_64" equals "1" then "x86_64" else the results from "uname -m"</li>
* <li>else the results from "uname -m"</li>
* </ul>
* </li>
* <li>For everything else - "unknown"</li>
* </ul>
* <dl>
*
*/
private void loadProperties(IProgressMonitor monitor) throws RemoteConnectionException {
SubMonitor subMon = SubMonitor.convert(monitor, 100);
fProperties.put(IRemoteConnection.FILE_SEPARATOR_PROPERTY, "/"); //$NON-NLS-1$
fProperties.put(IRemoteConnection.PATH_SEPARATOR_PROPERTY, ":"); //$NON-NLS-1$
fProperties.put(IRemoteConnection.LINE_SEPARATOR_PROPERTY, "\n"); //$NON-NLS-1$
fProperties.put(IRemoteConnection.USER_HOME_PROPERTY, getWorkingDirectory());
String osVersion;
String osArch;
String encoding = DEFAULT_ENCODING;
String osName = executeCommand("uname", subMon.newChild(10)); //$NON-NLS-1$
switch (osName.toLowerCase()) {
case "linux": //$NON-NLS-1$
osArch = executeCommand("uname -m", subMon.newChild(10)); //$NON-NLS-1$
osVersion = executeCommand("uname -r", subMon.newChild(10)); //$NON-NLS-1$
try {
encoding = executeCommand("locale charmap", subMon.newChild(10)); //$NON-NLS-1$
} catch (RemoteConnectionException e) {
// Use default
}
break;
case "darwin": //$NON-NLS-1$
osName = executeCommand("sw_vers -productName", subMon.newChild(10)); //$NON-NLS-1$
osVersion = executeCommand("sw_vers -productVersion", subMon.newChild(10)); //$NON-NLS-1$
osArch = executeCommand("uname -m", subMon.newChild(10)); //$NON-NLS-1$
if (osArch.equalsIgnoreCase("i386")) { //$NON-NLS-1$
String opt = executeCommand("sysctl -n hw.optional.x86_64", subMon.newChild(10)); //$NON-NLS-1$
if (opt.equals("1")) { //$NON-NLS-1$
osArch = "x86_64"; //$NON-NLS-1$
}
}
try {
encoding = executeCommand("locale charmap", subMon.newChild(10)); //$NON-NLS-1$
} catch (RemoteConnectionException e) {
// Use default
}
break;
case "aix": //$NON-NLS-1$
osArch = executeCommand("uname -p", subMon.newChild(10)); //$NON-NLS-1$
osVersion = executeCommand("oslevel", subMon.newChild(10)); //$NON-NLS-1$
if (osArch.equalsIgnoreCase("powerpc")) { //$NON-NLS-1$
/* Make the architecture match what Linux produces: either ppc or ppc64 */
osArch = "ppc"; //$NON-NLS-1$
/* Get Kernel type either 32-bit or 64-bit */
String opt = executeCommand("prtconf -k", subMon.newChild(10)); //$NON-NLS-1$
if (opt.indexOf("64-bit") > 0) { //$NON-NLS-1$
osArch += "64"; //$NON-NLS-1$
}
}
try {
encoding = executeCommand("locale charmap", subMon.newChild(10)); //$NON-NLS-1$
} catch (RemoteConnectionException e) {
// Use default
}
break;
case "qnx": //$NON-NLS-1$
osArch = executeCommand("uname -p", subMon.newChild(10)); //$NON-NLS-1$
osVersion = executeCommand("uname -r", subMon.newChild(10)); //$NON-NLS-1$
break;
default:
osVersion = "unknown"; //$NON-NLS-1$
osArch = "unknown"; //$NON-NLS-1$
encoding = "unknown"; //$NON-NLS-1$
break;
}
fProperties.put(IRemoteConnection.OS_NAME_PROPERTY, osName);
fProperties.put(IRemoteConnection.OS_VERSION_PROPERTY, osVersion);
fProperties.put(IRemoteConnection.OS_ARCH_PROPERTY, osArch);
fProperties.put(IRemoteConnection.LOCALE_CHARMAP_PROPERTY, encoding);
}
private Session newSession(IProgressMonitor monitor) throws RemoteConnectionException {
SubMonitor progress = SubMonitor.convert(monitor, 10);
try {
IRemoteConnectionWorkingCopy wc = getRemoteConnection().getWorkingCopy();
IRemoteConnectionHostService hostService = wc.getService(IRemoteConnectionHostService.class);
IUserAuthenticatorService authService = wc.getService(IUserAuthenticatorService.class);
Session session = fJSchService.createSession(hostService.getHostname(), hostService.getPort(), hostService.getUsername());
session.setUserInfo(new JSchUserInfo(hostService, authService));
if (hostService.usePassword()) {
session.setConfig("PreferredAuthentications", "password,keyboard-interactive,gssapi-with-mic,publickey"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
session.setConfig("PreferredAuthentications", "publickey,gssapi-with-mic,password,keyboard-interactive"); //$NON-NLS-1$ //$NON-NLS-2$
}
String password = hostService.getPassword();
if (!password.isEmpty()) {
session.setPassword(password);
}
if (getProxyCommand().isEmpty() && getProxyConnectionName().isEmpty()) {
fJSchService.connect(session, getTimeout() * 1000, progress.newChild(10)); // connect without proxy
} else {
if (getProxyCommand().isEmpty()) {
session.setProxy(JSchConnectionProxyFactory.createForwardProxy(getProxyConnection(), progress.newChild(10)));
fJSchService.connect(session, getTimeout() * 1000, progress.newChild(10));
} else {
session.setProxy(JSchConnectionProxyFactory.createCommandProxy(getProxyConnection(), getProxyCommand(),
progress.newChild(10)));
session.connect(getTimeout() * 1000); // the fJSchService doesn't pass the timeout correctly
}
}
if (progress.isCanceled()) {
return null;
}
wc.save();
fSessions.add(session);
return session;
} catch (OperationCanceledException e) {
throw new RemoteConnectionException(Messages.JSchConnection_0);
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
@Override
public void open(IProgressMonitor monitor) throws RemoteConnectionException {
open(monitor, true);
}
/**
* Open ssh connection without full setup (environment, sftp)
*
* @see org.eclipse.remote.core.IRemoteConnection#open()
*
* @param monitor
* @throws RemoteConnectionException
*/
public void openMinimal(IProgressMonitor monitor) throws RemoteConnectionException {
open(monitor, false);
}
/**
* @see org.eclipse.remote.core.IRemoteConnection#open()
*
* @param monitor
* @param setupFully
* open a full featured connection (environment query and sftp)
* @throws RemoteConnectionException
*/
private void open(IProgressMonitor monitor, boolean setupFully) throws RemoteConnectionException {
SubMonitor subMon = SubMonitor.convert(monitor, 60);
if (!hasOpenSession()) {
checkIsConfigured();
newSession(subMon.newChild(10));
if (subMon.isCanceled()) {
throw new RemoteConnectionException(Messages.JSchConnection_Connection_was_cancelled);
}
isFullySetup = false;
}
if (setupFully && !isFullySetup) { // happens on the first open with setupFully==true, which might not be the first open
isFullySetup = true;
// getCwd checks the exec channel before checkConfiguration checks the sftp channel
fWorkingDir = getCwd(subMon.newChild(10));
try {
if (!checkConfiguration(fSessions.get(0), subMon.newChild(20))) {
newSession(subMon.newChild(10));
loadEnv(subMon.newChild(10));
}
} catch (RemoteConnectionException e) {
// Do not throw exception now, it will be thrown if FileService is accessed.
}
loadProperties(subMon.newChild(10));
fRemoteConnection.fireConnectionChangeEvent(RemoteConnectionChangeEvent.CONNECTION_OPENED);
}
}
private ChannelSftp openSftpChannel(Session session) throws RemoteConnectionException {
try {
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); //$NON-NLS-1$
channel.connect();
return channel;
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
@Override
public void removeLocalPortForwarding(int port) throws RemoteConnectionException {
if (!isOpen()) {
throw new RemoteConnectionException(Messages.JSchConnection_connectionNotOpen);
}
try {
fSessions.get(0).delPortForwardingL(port);
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
@Override
public void removeRemotePortForwarding(int port) throws RemoteConnectionException {
if (!isOpen()) {
throw new RemoteConnectionException(Messages.JSchConnection_connectionNotOpen);
}
try {
fSessions.get(0).delPortForwardingR(port);
} catch (JSchException e) {
throw new RemoteConnectionException(e.getMessage());
}
}
@Override
public void setWorkingDirectory(String path) {
if (RemoteServicesUtils.posixPath(path).isAbsolute()) {
fWorkingDir = path;
}
}
@Override
public boolean useLoginShell() {
String str = fRemoteConnection.getAttribute(USE_LOGIN_SHELL_ATTR);
return !str.isEmpty() ? Boolean.parseBoolean(str) : DEFAULT_USE_LOGIN_SHELL;
}
@Override
public void setHostname(String hostname) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setAttribute(ADDRESS_ATTR, hostname);
}
}
@Override
public void setPassphrase(String passphrase) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setSecureAttribute(PASSPHRASE_ATTR, passphrase);
}
}
@Override
public void setPassword(String password) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setSecureAttribute(PASSWORD_ATTR, password);
}
}
@Override
public void setPort(int port) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setAttribute(PORT_ATTR, Integer.toString(port));
}
}
@Override
public void setTimeout(int timeout) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setAttribute(TIMEOUT_ATTR, Integer.toString(timeout));
}
}
@Override
public void setUseLoginShell(boolean useLogingShell) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setAttribute(USE_LOGIN_SHELL_ATTR, Boolean.toString(useLogingShell));
}
}
@Override
public void setUsePassword(boolean usePassword) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setAttribute(IS_PASSWORD_ATTR, Boolean.toString(usePassword));
}
}
@Override
public void setUsername(String username) {
if (fRemoteConnection instanceof IRemoteConnectionWorkingCopy) {
IRemoteConnectionWorkingCopy wc = (IRemoteConnectionWorkingCopy) fRemoteConnection;
wc.setAttribute(USERNAME_ATTR, username);
}
}
}