1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-08 18:26:01 +02:00

Bug 345329 - Add SSH proxy

Allows to connect over any ssh server as gateway or over any other
proxy by executing a local or remote command (such as netcat, corkscrew, ...).

Change-Id: I413c22cb588d8560d8db9ac877fb04f8331456aa
This commit is contained in:
Roland Schulz 2014-09-30 00:36:51 -04:00 committed by John Eblen
parent 5ee4fa818a
commit c563ea3f0e
10 changed files with 893 additions and 15 deletions

View file

@ -0,0 +1,367 @@
/*******************************************************************************
* Copyright (c) 2007 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
*******************************************************************************/
package org.eclipse.remote.internal.jsch.core;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Utilitiy class for managing command line arguments.
*
*/
public class ArgumentParser {
private static List<String> parseCommandline(String commandline) {
ArrayList<String> result = new ArrayList<String>();
StringCharacterIterator iterator = new StringCharacterIterator(commandline);
for (iterator.first(); iterator.current() != CharacterIterator.DONE; iterator.next()) {
// Restart to skip white space
if (Character.isWhitespace(iterator.current())) {
continue;
}
// Read token
StringBuffer buffer = new StringBuffer();
token_reader: for (; iterator.current() != CharacterIterator.DONE; iterator.next()) {
char tokenChar = iterator.current();
// A white space terminates the token
if (Character.isWhitespace(tokenChar)) {
break token_reader;
}
// Handle character that composes the token
switch (tokenChar) {
case '"': {
/*
* Read all text within double quotes or until end of
* string. Allows escaping.
*/
iterator.next(); // Skip quote
quoted_reader: while ((iterator.current() != CharacterIterator.DONE) && (iterator.current() != '"')) {
char innerChar = iterator.current();
switch (innerChar) {
case '\\':
char nextChar = iterator.next();
switch (nextChar) {
case CharacterIterator.DONE:
break quoted_reader;
case '"':
// Add the character, but remove the escape
buffer.append(nextChar);
iterator.next();
continue quoted_reader;
default:
// Add the character and keep escape
buffer.append(innerChar);
buffer.append(nextChar);
iterator.next();
continue quoted_reader;
}
default:
buffer.append(innerChar);
iterator.next();
continue quoted_reader;
}
}
continue token_reader;
}
case '\'': {
/*
* Read all text within single quotes or until end of
* string. No escaping.
*/
iterator.next(); // Skip the quote
while ((iterator.current() != CharacterIterator.DONE) && (iterator.current() != '\'')) {
buffer.append(iterator.current());
iterator.next();
}
continue token_reader;
}
case '\\': {
/*
* Read escaped char.
*/
char nextChar = iterator.next();
switch (nextChar) {
case CharacterIterator.DONE:
break token_reader;
case '\n':
// Ignore newline. Both lines are concatenated.
continue token_reader;
default:
// Add the character, but remove the escape
buffer.append(nextChar);
continue token_reader;
}
}
default:
/*
* Any other char, add to the buffer.
*/
buffer.append(tokenChar);
continue token_reader;
}
}
result.add(buffer.toString());
}
return result;
}
private final List<String> tokens;
/**
* Create a command line representation from an array of strings. The first
* element of the list is assumed to be the command, the remaining, the
* arguments. The elements are not parsed not (un)escaped., but taked as the
* are.
*
*/
public ArgumentParser(List<String> tokenList) {
this.tokens = new ArrayList<String>(tokenList);
}
/**
* Create a command line representation from the string with a shell command
* line. The command line is parsed and split on spaces. Quoted or escaped
* spaces are preserved..
*
*/
public ArgumentParser(String commandline) {
this.tokens = parseCommandline(commandline);
}
/**
* Create a command line representation from an array of strings. The first
* element of the array is assumed to be the command, the remaining, the
* arguments. The elements are not parsed not (un)escaped., but taked as the
* are.
*
*/
public ArgumentParser(String tokenArray[]) {
this(Arrays.asList(tokenArray));
}
/**
* Create a command line representation from the command and an list of
* parameters. The elements are not parsed not (un)escaped., but taked as
* the are.
*
*/
public ArgumentParser(String command, List<String> parameterList) {
this.tokens = new ArrayList<String>();
this.tokens.add(command);
this.tokens.addAll(parameterList);
}
/**
* Create a command line representation from the command and an array of
* parameters. The elements are not parsed not (un)escaped., but taked as
* the are.
*
*/
public ArgumentParser(String command, String parameterArray[]) {
this(command, Arrays.asList(parameterArray));
}
private StringBuffer escapeToken(String token, boolean fullEscape) {
StringBuffer buffer = new StringBuffer();
StringCharacterIterator iter = new StringCharacterIterator(token);
for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
if (Character.isWhitespace(c)) {
buffer.append('\\');
buffer.append(c);
continue;
}
switch (c) {
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '|':
case '\\':
case '*':
case '&':
case '^':
case '%':
case '$':
case '#':
case '@':
case '!':
case '~':
case '`':
case '\'':
case '"':
case ':':
case ';':
case '?':
case '>':
case '<':
case '\n':
if (fullEscape) {
buffer.append('\\');
}
buffer.append(c);
continue;
case ' ':
buffer.append('\\');
buffer.append(c);
continue;
default:
buffer.append(c);
continue;
}
}
return buffer;
}
/**
* Returns the command of the command line, assuming that the first entry is
* always the command.
*
* @return The command or null if the command lines has no command nor
* arguments.
*/
public String getCommand() {
if (this.tokens.size() == 0) {
return null;
}
return this.tokens.get(0);
}
/**
* Convert all tokens in a full command line that can be executed in a
* shell.
*
* @param fullEscape
* If every special character shall be escaped. If false, only
* white spaces are escaped and the shell will interpret the
* special chars. If true, then all special chars are quoted.
*/
public String getCommandLine(boolean fullEscape) {
StringBuffer buffer = new StringBuffer();
Iterator<String> iterator = this.tokens.iterator();
boolean first = true;
while (iterator.hasNext()) {
String token = iterator.next();
if (!first) {
buffer.append(' ');
} else {
first = false;
}
buffer.append(escapeToken(token, fullEscape));
}
return buffer.toString();
}
/**
* Returns the command of the command line, assuming that the first entry is
* always the command.
*
* @return The command or null if the command lines has no command nor
* arguments.
* @param fullEscape
* If every special character shall be escaped. If false, only
* white spaces are escaped and the shell will interpret the
* special chars. If true, then all special chars are quoted.
*/
public String getEscapedCommand(boolean fullEscalpe) {
if (this.tokens.size() == 0) {
return null;
}
return escapeToken(this.tokens.get(0), fullEscalpe).toString();
}
/**
* Returns a list of all arguments, assuming that the first entry is the
* command name.
*
* @return The Array or null if the command lines has no command nor
* arguments.
*/
public String[] getParameterArray() {
if (this.tokens.size() == 0) {
return null;
}
return this.tokens.subList(1, this.tokens.size()).toArray(new String[this.tokens.size() - 1]);
}
/**
* Returns a list of all arguments, assuming that the first entry is the
* command name.
*
* @return The List or null if the command lines has no command nor
* arguments.
*/
public List<String> getParameterList() {
if (this.tokens.size() == 0) {
return null;
}
return new ArrayList<String>(this.tokens.subList(1, this.tokens.size()));
}
/**
* Returns the total number of entries.
*
* @return
*/
public int getSize() {
return this.tokens.size();
}
/**
* Returns a List of all entries of the command line.
*
* @return The Array
*/
public String[] getTokenArray() {
return this.tokens.toArray(new String[this.tokens.size()]);
}
/**
* Returns a List of all entries of the command line.
*
* @return The List
*/
public List<String> getTokenList() {
return new ArrayList<String>(this.tokens);
}
/**
* Returns a representation of the command line for debug purposes.
*/
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("<"); //$NON-NLS-1$
Iterator<String> iterator = this.tokens.iterator();
boolean first = true;
while (iterator.hasNext()) {
String token = iterator.next();
if (!first) {
buffer.append('\n');
} else {
first = false;
}
buffer.append(token);
}
buffer.append(">"); //$NON-NLS-1$
return buffer.toString();
}
}

View file

@ -27,18 +27,21 @@ import org.eclipse.osgi.util.NLS;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteConnectionChangeEvent;
import org.eclipse.remote.core.IRemoteConnectionChangeListener;
import org.eclipse.remote.core.IRemoteConnectionManager;
import org.eclipse.remote.core.IRemoteConnectionWorkingCopy;
import org.eclipse.remote.core.IRemoteFileManager;
import org.eclipse.remote.core.IRemoteProcess;
import org.eclipse.remote.core.IRemoteProcessBuilder;
import org.eclipse.remote.core.IRemoteServices;
import org.eclipse.remote.core.IUserAuthenticator;
import org.eclipse.remote.core.RemoteServices;
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.JSchException;
@ -388,7 +391,7 @@ public class JSchConnection implements IRemoteConnection {
/*
* (non-Javadoc)
*
* @see org.eclipse.remote.core.IRemoteConnection#forwardRemotePort(java. lang.String, int,
* @see org.eclipse.remote.core.IRemoteConnection#forwardRemotePort(java.lang.String, int,
* org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
@ -581,6 +584,38 @@ public class JSchConnection implements IRemoteConnection {
return fProperties.get(key);
}
/**
* Gets the proxy command. For no proxy command an empty string is returned.
*
* @return proxy command
*/
public String getProxyCommand() {
return fAttributes.getAttribute(JSchConnectionAttributes.PROXYCOMMAND_ATTR, EMPTY_STRING);
}
/**
* Gets the proxy connection. If no proxy is used it returns a local connection.
*
* @return proxy connection
*/
public IRemoteConnection getProxyConnection() {
String proxyConnectionName = getProxyConnectionName();
if (proxyConnectionName.equals(EMPTY_STRING)) {
return RemoteServices.getLocalServices().getConnectionManager().getConnection(
IRemoteConnectionManager.LOCAL_CONNECTION_NAME);
}
return fManager.getConnection(proxyConnectionName);
}
/**
* Gets the proxy connection name
*
* @return proxy connection name
*/
public String getProxyConnectionName() {
return fAttributes.getAttribute(JSchConnectionAttributes.PROXYCONNECTION_ATTR, EMPTY_STRING);
}
/*
* (non-Javadoc)
*
@ -612,6 +647,22 @@ public class JSchConnection implements IRemoteConnection {
return fSftpChannel;
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Session#getStreamForwarder(java.lang.String, int)
*/
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);
}
}
public int getTimeout() {
return fAttributes.getInt(JSchConnectionAttributes.TIMEOUT_ATTR, DEFAULT_TIMEOUT);
}
@ -792,7 +843,19 @@ public class JSchConnection implements IRemoteConnection {
if (isPasswordAuth()) {
session.setPassword(getPassword());
}
if (getProxyCommand().equals(EMPTY_STRING) && getProxyConnectionName().equals(EMPTY_STRING)) {
fJSchService.connect(session, getTimeout() * 1000, progress.newChild(10)); // connect without proxy
} else {
if (getProxyCommand().equals(EMPTY_STRING)) {
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()) {
fSessions.add(session);
return session;

View file

@ -31,6 +31,8 @@ public class JSchConnectionAttributes {
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 KEYFILE_ATTR = "JSCH_KEYFILE_ATTR"; //$NON-NLS-1$

View file

@ -0,0 +1,311 @@
/*******************************************************************************
* Copyright (c) 2014 University of Tennessee 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:
* University of Tennessee (Roland Schulz) - Initial API and implementation
*******************************************************************************/
package org.eclipse.remote.internal.jsch.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.List;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteProcess;
import org.eclipse.remote.core.IRemoteProcessBuilder;
import org.eclipse.remote.core.exception.RemoteConnectionException;
import org.eclipse.remote.internal.jsch.core.messages.Messages;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.Proxy;
import com.jcraft.jsch.SocketFactory;
/**
* Creates a JSch Proxy. Supports both command proxies, as well as the ssh build-in
* stream forwarding.
*
* @author rschulz
*
*/
public class JSchConnectionProxyFactory {
private static class CommandProxy implements Proxy {
private String command;
private IRemoteProcess process;
private IRemoteConnection connection;
private IProgressMonitor monitor;
private CommandProxy(IRemoteConnection connection, String command, IProgressMonitor monitor) {
if (command == null || connection == null || monitor == null)
throw new IllegalArgumentException();
this.command = command;
this.connection = connection;
this.monitor = monitor;
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#close()
*/
@Override
public void close() {
process.destroy();
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#connect(com.jcraft.jsch.SocketFactory, java.lang.String, int, int)
*/
@Override
public void connect(SocketFactory socket_factory, String host,
int port, int timeout) throws IOException {
assert connection != null : "connect should only be called once"; //$NON-NLS-1$
try {
if (timeout == 0) {
timeout = 10000; // default to 10s
}
final int waitTime = 50;
final int waitSteps = timeout / waitTime;
SubMonitor subMon;
// Open connection if it isn't already opened
if (!connection.isOpen()) {
subMon = SubMonitor.convert(monitor, waitSteps * 2);
try {
connection.open(subMon.newChild(waitSteps));
} catch (RemoteConnectionException e) {
throw new IOException(e);
}
} else {
subMon = SubMonitor.convert(monitor, waitSteps);
}
// Start command
command = command.replace("%h", host); //$NON-NLS-1$
command = command.replace("%p", Integer.toString(port)); //$NON-NLS-1$
List<String> cmd = new ArgumentParser(command).getTokenList();
IRemoteProcessBuilder pb = connection.getProcessBuilder(cmd);
process = pb.start();
// Wait on command to produce stdout output
long endTime = System.currentTimeMillis() + timeout;
boolean bOutputAvailable, bProcessComplete, bTimedOut, bCanceled;
do {
try {
Thread.sleep(waitTime);
subMon.worked(1);
} catch (InterruptedException e) {
/* ignore */
}
bOutputAvailable = (getInputStream().available() != 0);
bProcessComplete = process.isCompleted();
bTimedOut = System.currentTimeMillis() > endTime;
bCanceled = subMon.isCanceled();
} while (!bOutputAvailable && !bProcessComplete && !bTimedOut && !bCanceled);
// If no output was produced before process died, throw an exception with the stderr output
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
if (getInputStream().available() == 0 || process.isCompleted()) {
String msg = ""; //$NON-NLS-1$
while (bufferedReader.ready()) {
msg += (char) bufferedReader.read();
}
msg = msg.trim();
if (!process.isCompleted()) {
process.destroy();
}
String cause = Messages.JSchConnectionProxyFactory_failed;
if (bTimedOut) {
cause = Messages.JSchConnectionProxyFactory_timedOut;
} else if (bCanceled) {
cause = Messages.JSchConnectionProxyFactory_wasCanceled;
}
throw new IOException(MessageFormat.format(Messages.JSchConnectionProxyFactory_ProxyCommandFailed,
command, cause, msg));
}
// Dump the stderr to log
new Thread() {
@Override
public void run() {
final ILog log = Activator.getDefault().getLog();
String line;
try {
while ((line = bufferedReader.readLine()) != null) {
log.log(new Status(IStatus.INFO, Activator.getUniqueIdentifier(),
IStatus.OK, line, null));
}
} catch (IOException e) {
Activator.log(e);
}
};
}.start();
} finally {
// Not valid to call connect again or for any other command to access these variables. Setting to null to ensure.
connection = null;
}
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#getInputStream()
*/
@Override
public InputStream getInputStream() {
return process.getInputStream();
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#getOutputStream()
*/
@Override
public OutputStream getOutputStream() {
return process.getOutputStream();
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#getSocket()
*/
@Override
public Socket getSocket() {
return null;
}
}
private static class SSHForwardProxy implements Proxy {
private Channel channel;
private JSchConnection connection;
private IProgressMonitor monitor;
private SSHForwardProxy(IRemoteConnection proxyConnection, IProgressMonitor monitor) {
if (proxyConnection == null || monitor == null)
throw new IllegalArgumentException();
this.connection = (JSchConnection) proxyConnection; // only JSch supported for ForwardProxy
this.monitor = monitor;
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#close()
*/
@Override
public void close() {
channel.disconnect();
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#connect(com.jcraft.jsch.SocketFactory, java.lang.String, int, int)
*/
@Override
public void connect(SocketFactory socket_factory, String host, int port,
int timeout) throws Exception {
assert connection != null : "connect should only be called once"; //$NON-NLS-1$
try {
if (!connection.isOpen()) {
try {
connection.open(monitor);
} catch (RemoteConnectionException e) {
throw new IOException(e);
}
}
channel = connection.getStreamForwarder(host, port);
} finally {
// Not valid to call connect again or for any other command to access these variables. Setting to null to ensure.
connection = null;
}
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#getInputStream()
*/
@Override
public InputStream getInputStream() {
try {
return channel.getInputStream();
} catch (IOException e) {
Activator.log(e);
return null;
}
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#getOutputStream()
*/
@Override
public OutputStream getOutputStream() {
try {
return channel.getOutputStream();
} catch (IOException e) {
Activator.log(e);
return null;
}
}
/*
* (non-Javadoc)
*
* @see com.jcraft.jsch.Proxy#getSocket()
*/
@Override
public Socket getSocket() {
return null;
}
}
/**
* Creates a (local or remote) command proxy.
*
* @param connection
* Any (local or remote) connection. Cannot be null.
* @param command
* A valid proxy command. Cannot be null or empty.
* @param monitor
* A valid progress monitor. Cannot be null.
* @return ssh proxy
*/
public static Proxy createCommandProxy(IRemoteConnection connection, String command, IProgressMonitor monitor) {
return new CommandProxy(connection, command, monitor);
}
/**
* Creates a ssh forward proxy.
*
* @param proxyConnection
* The Jsch proxy connection. Cannot be null.
* @param monitor
* A valid progress monitor. Cannot be null.
* @return ssh proxy
*/
public static Proxy createForwardProxy(IRemoteConnection proxyConnection, IProgressMonitor monitor) {
return new SSHForwardProxy(proxyConnection, monitor);
}
}

View file

@ -107,6 +107,26 @@ public class JSchConnectionWorkingCopy extends JSchConnection implements IRemote
return fWorkingAttributes.getInt(JSchConnectionAttributes.PORT_ATTR, DEFAULT_PORT);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.remote.internal.jsch.core.JSchConnection#getProxyCommand()
*/
@Override
public String getProxyCommand() {
return fWorkingAttributes.getAttribute(JSchConnectionAttributes.PROXYCOMMAND_ATTR, EMPTY_STRING);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.remote.internal.jsch.core.JSchConnection#getProxyConnectionName()
*/
@Override
public String getProxyConnectionName() {
return fWorkingAttributes.getAttribute(JSchConnectionAttributes.PROXYCONNECTION_ATTR, EMPTY_STRING);
}
/*
* (non-Javadoc)
*
@ -255,6 +275,28 @@ public class JSchConnectionWorkingCopy extends JSchConnection implements IRemote
fWorkingAttributes.setAttribute(JSchConnectionAttributes.PORT_ATTR, Integer.toString(port));
}
/**
* Sets the proxy command. Set to empty string for no command
*
* @param proxyCommand
* proxy command
*/
public void setProxyCommand(String proxyCommand) {
fIsDirty = true;
fWorkingAttributes.setAttribute(JSchConnectionAttributes.PROXYCOMMAND_ATTR, proxyCommand);
}
/**
* Sets the proxy connection name. Set to empty string for no proxy connection
*
* @param proxyCommand
* proxy connection name
*/
public void setProxyConnectionName(String proxyConnectionName) {
fIsDirty = true;
fWorkingAttributes.setAttribute(JSchConnectionAttributes.PROXYCONNECTION_ATTR, proxyConnectionName);
}
public void setTimeout(int timeout) {
fIsDirty = true;
fWorkingAttributes.setAttribute(JSchConnectionAttributes.TIMEOUT_ATTR, Integer.toString(timeout));

View file

@ -38,6 +38,10 @@ public class Messages extends NLS {
public static String JSchConnectionManager_connection_with_name_exists;
public static String JSchConnectionManager_cannotRemoveOpenConnection;
public static String JSchConnectionManager_invalidConnectionType;
public static String JSchConnectionProxyFactory_failed;
public static String JSchConnectionProxyFactory_ProxyCommandFailed;
public static String JSchConnectionProxyFactory_timedOut;
public static String JSchConnectionProxyFactory_wasCanceled;
public static String JSchProcessBuilder_Connection_is_not_open;
public static String JschFileStore_Connection_is_not_open;
public static String JschFileStore_File_doesnt_exist;

View file

@ -28,6 +28,10 @@ JSchConnection_username_must_be_set=Username must be set before opening connecti
JSchConnectionManager_connection_with_name_exists=A connection with name \"{0}\" already exists
JSchConnectionManager_cannotRemoveOpenConnection=Cannot remove an open connection
JSchConnectionManager_invalidConnectionType=Invalid connection type
JSchConnectionProxyFactory_failed=failed
JSchConnectionProxyFactory_ProxyCommandFailed=Proxy command "{0}" {1} and printed message "{2}"
JSchConnectionProxyFactory_timedOut=timed out
JSchConnectionProxyFactory_wasCanceled=was canceled
JSchProcessBuilder_Connection_is_not_open=Connection is not open
JschFileStore_Connection_is_not_open=Connection is not open
JschFileStore_File_doesnt_exist=File {0} doesn't exist

View file

@ -25,6 +25,12 @@ public class Messages extends NLS {
public static String JSchConnectionPage_Edit_Connection;
public static String JSchConnectionPage_Edit_properties_of_an_existing_connection;
public static String JSchConnectionPage_Please_enter_name_for_connection;
public static String JSchConnectionPage_Proxy;
public static String JSchConnectionPage_Help;
public static String JSchConnectionPage_SelectCommand;
public static String JSchConnectionPage_SelectConnection;
public static String JSchConnectionPage_Settings0;
public static String JSchConnectionPage_selectProxyConnection;
public static String JSchFileSystemContributor_0;
public static String JSchNewConnectionPage_Advanced;
public static String JSchNewConnectionPage_Connection_name;

View file

@ -12,6 +12,12 @@ JSchConnectionPage_A_connection_with_that_name_already_exists=A connection with
JSchConnectionPage_Edit_Connection=Edit Connection
JSchConnectionPage_Edit_properties_of_an_existing_connection=Edit properties of an existing connection
JSchConnectionPage_Please_enter_name_for_connection=Please enter a name for the connection
JSchConnectionPage_Proxy=SSH Proxy Settings
JSchConnectionPage_Help=If 'Local' is selected and proxy command is empty, no proxy is used.\nSee <a href=\"org.eclipse.ui.net.NetPreferences\">Network Connections</a> for SOCKS and HTTP proxy options.
JSchConnectionPage_SelectCommand=Enter a local or remote command such as 'nc %h %p'. Can be empty for an ssh gateway.
JSchConnectionPage_SelectConnection=Select 'Remote' for an ssh gateway or a remote proxy command.
JSchConnectionPage_Settings0=Connection Settings
JSchConnectionPage_selectProxyConnection=Please select a proxy connection
JSchFileSystemContributor_0=Browse File System
JSchNewConnectionPage_Advanced=Advanced
JSchNewConnectionPage_Connection_name=Connection name:

View file

@ -17,6 +17,7 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteConnectionManager;
import org.eclipse.remote.core.RemoteServices;
import org.eclipse.remote.core.exception.RemoteConnectionException;
@ -25,6 +26,7 @@ import org.eclipse.remote.internal.jsch.core.JSchConnection;
import org.eclipse.remote.internal.jsch.core.JSchConnectionAttributes;
import org.eclipse.remote.internal.jsch.core.JSchConnectionWorkingCopy;
import org.eclipse.remote.internal.jsch.ui.messages.Messages;
import org.eclipse.remote.ui.widgets.RemoteConnectionWidget;
import org.eclipse.remote.ui.widgets.RemoteFileWidget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
@ -41,6 +43,7 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.IExpansionListener;
@ -74,6 +77,8 @@ public class JSchConnectionPage extends WizardPage {
private final IRemoteConnectionManager fConnectionManager;
private final DataModifyListener fDataModifyListener = new DataModifyListener();
private RemoteConnectionWidget fProxyConnectionWidget;
private Text fProxyCommandText;
public JSchConnectionPage(IRemoteConnectionManager connMgr) {
super(Messages.JSchNewConnectionPage_New_Connection);
@ -82,7 +87,7 @@ public class JSchConnectionPage extends WizardPage {
}
/**
* Create controls for the bottom (hideable) composite
* Create controls for the bottom (hideable) advanced composite
*
* @param mold
*
@ -96,6 +101,7 @@ public class JSchConnectionPage extends WizardPage {
@Override
public void expansionStateChanged(ExpansionEvent e) {
for (int i = 0; i < 2; i++) { // sometimes the size compute isn't correct on first try
Point newSize = parent.computeSize(SWT.DEFAULT, SWT.DEFAULT);
Point currentSize = parent.getSize();
int deltaY = newSize.y - currentSize.y;
@ -104,6 +110,7 @@ public class JSchConnectionPage extends WizardPage {
getShell().setSize(shellSize);
getShell().layout(true, true);
}
}
@Override
public void expansionStateChanging(ExpansionEvent e) {
@ -112,25 +119,36 @@ public class JSchConnectionPage extends WizardPage {
});
Composite advancedComp = new Composite(expComp, SWT.NONE);
advancedComp.setLayout(new GridLayout(2, false));
advancedComp.setLayout(new GridLayout(1, false));
advancedComp.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
Label portLabel = new Label(advancedComp, SWT.NONE);
Group settingsComp = new Group(advancedComp, SWT.NONE);
settingsComp.setText(Messages.JSchConnectionPage_Settings0);
settingsComp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
settingsComp.setLayout(new GridLayout(2, false));
Label portLabel = new Label(settingsComp, SWT.NONE);
portLabel.setText(Messages.JSchNewConnectionPage_Port);
portLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
fPortText = new Text(advancedComp, SWT.BORDER | SWT.SINGLE);
fPortText = new Text(settingsComp, SWT.BORDER | SWT.SINGLE);
fPortText.setText(Integer.toString(JSchConnection.DEFAULT_PORT));
fPortText.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
setTextFieldWidthInChars(fPortText, 5);
Label timeoutLabel = new Label(advancedComp, SWT.NONE);
Label timeoutLabel = new Label(settingsComp, SWT.NONE);
timeoutLabel.setText(Messages.JSchNewConnectionPage_Timeout);
timeoutLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
fTimeoutText = new Text(advancedComp, SWT.BORDER | SWT.SINGLE);
fTimeoutText = new Text(settingsComp, SWT.BORDER | SWT.SINGLE);
fTimeoutText.setText(Integer.toString(JSchConnection.DEFAULT_TIMEOUT));
fTimeoutText.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
setTextFieldWidthInChars(fTimeoutText, 5);
Group proxyComp = new Group(advancedComp, SWT.NONE);
proxyComp.setText(Messages.JSchConnectionPage_Proxy);
proxyComp.setLayout(new GridLayout(1, false));
proxyComp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
createProxyControls(proxyComp);
expComp.setClient(advancedComp);
}
@ -248,6 +266,33 @@ public class JSchConnectionPage extends WizardPage {
updateEnablement();
}
/**
* Create controls for the bottom (hideable) proxy composite
*
* @param mold
*
*/
private void createProxyControls(final Composite proxyComp) {
Label lblConnection = new Label(proxyComp, SWT.WRAP);
lblConnection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
lblConnection.setText(Messages.JSchConnectionPage_SelectConnection);
fProxyConnectionWidget = new RemoteConnectionWidget(proxyComp, SWT.NONE, null, 0, null);
Label lblCommand = new Label(proxyComp, SWT.WRAP);
lblCommand.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
lblCommand.setText(Messages.JSchConnectionPage_SelectCommand);
fProxyCommandText = new Text(proxyComp, SWT.BORDER);
fProxyCommandText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Link link = new Link(proxyComp, SWT.WRAP);
final GridData linkLayoutData = new GridData(GridData.FILL_HORIZONTAL);
link.setLayoutData(linkLayoutData);
linkLayoutData.widthHint = 400;
link.setText(Messages.JSchConnectionPage_Help);
}
public JSchConnectionWorkingCopy getConnection() {
return fConnection;
}
@ -285,6 +330,9 @@ public class JSchConnectionPage extends WizardPage {
fPassphraseText.setText(fConnection.getPassphrase());
fFileWidget.setLocationPath(fConnection.getKeyFile());
}
fProxyCommandText.setText(fConnection.getProxyCommand());
fProxyConnectionWidget.setConnection(fConnection.getProxyConnection());
} else {
fConnectionName.setText(fInitialName);
String host = fInitialAttributes.get(JSchConnectionAttributes.ADDRESS_ATTR);
@ -319,6 +367,8 @@ public class JSchConnectionPage extends WizardPage {
if (file != null) {
fFileWidget.setLocationPath(file);
}
fProxyConnectionWidget.setConnection(RemoteServices.getLocalServices().getConnectionManager().getConnection(
IRemoteConnectionManager.LOCAL_CONNECTION_NAME));
}
}
@ -331,6 +381,14 @@ public class JSchConnectionPage extends WizardPage {
fPassphraseText.addModifyListener(fDataModifyListener);
fPortText.addModifyListener(fDataModifyListener);
fTimeoutText.addModifyListener(fDataModifyListener);
fProxyCommandText.addModifyListener(fDataModifyListener);
fProxyConnectionWidget.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
validateFields();
getContainer().updateButtons();
}
});
}
public void setAddress(String address) {
@ -420,6 +478,17 @@ public class JSchConnectionPage extends WizardPage {
if (fConnection.getPort() != port) {
fConnection.setPort(port);
}
if (!fConnection.getProxyCommand().equals(fProxyCommandText.getText().trim())) {
fConnection.setProxyCommand(fProxyCommandText.getText().trim());
}
IRemoteConnection proxyConnection = fProxyConnectionWidget.getConnection();
String proxyConnectionName = ""; //$NON-NLS-1$
if (proxyConnection != null && proxyConnection.getRemoteServices() != RemoteServices.getLocalServices()) {
proxyConnectionName = proxyConnection.getName();
}
if (!fConnection.getProxyConnectionName().equals(proxyConnectionName)) {
fConnection.setProxyConnectionName(proxyConnectionName);
}
}
}
@ -461,9 +530,13 @@ public class JSchConnectionPage extends WizardPage {
if (message == null) {
message = validatePasskey();
}
if (message == null && fProxyConnectionWidget.getConnection() == null) {
message = Messages.JSchConnectionPage_selectProxyConnection;
}
if (message == null) {
message = validateAdvanced();
}
setErrorMessage(message);
setPageComplete(message == null);
}