1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-10 17:55:39 +02:00

fix for 149785. Confine allowed port range to daemon-specified range.

This commit is contained in:
David McKnight 2006-08-21 15:17:21 +00:00
parent 6d562f92c6
commit d2fcb558d4
5 changed files with 223 additions and 207 deletions

View file

@ -25,6 +25,7 @@ public interface IDataStoreConstants
public static final String UNKNOWN_PROBLEM = "unknown problem connecting to server"; public static final String UNKNOWN_PROBLEM = "unknown problem connecting to server";
public static final String SERVER_FAILURE = "server failure: "; public static final String SERVER_FAILURE = "server failure: ";
public static final String ATTEMPT_RECONNECT = "attempt reconnect"; public static final String ATTEMPT_RECONNECT = "attempt reconnect";
public static final String PORT_OUT_RANGE = "specified port out of range:";
public static final String DATASTORE_SPIRIT_DESCRIPTOR = "datastore.spirit"; public static final String DATASTORE_SPIRIT_DESCRIPTOR = "datastore.spirit";
public static final String C_START_SPIRIT = "C_START_SPIRIT"; public static final String C_START_SPIRIT = "C_START_SPIRIT";

View file

@ -41,52 +41,55 @@ import org.eclipse.dstore.core.model.IDataStoreConstants;
import org.eclipse.dstore.core.model.ISSLProperties; import org.eclipse.dstore.core.model.ISSLProperties;
import org.eclipse.dstore.core.util.ssl.DStoreSSLContext; import org.eclipse.dstore.core.util.ssl.DStoreSSLContext;
/** /**
* This class is the DataStore daemon. It is used for authenticating users, * This class is the DataStore daemon. It is used for authenticating users,
* launching DataStore servers under particular user IDs, and providing a * launching DataStore servers under particular user IDs, and providing a client
* client with enough information to conntect to a launched server. * with enough information to conntect to a launched server.
*/ */
public class ServerLauncher extends Thread public class ServerLauncher extends Thread {
{
/** /**
* An instances of this class get loaded whenever a client requests access * An instances of this class get loaded whenever a client requests access
* to a DataStore server. The ConnectionListener attempts to launch a server * to a DataStore server. The ConnectionListener attempts to launch a server
* under the client user's ID, communicating back information to the client * under the client user's ID, communicating back information to the client
* so that if may connect to the launched server. If the authentification and * so that if may connect to the launched server. If the authentification
* connection to the server are successful, ConnectionListener continues to * and connection to the server are successful, ConnectionListener continues
* monitor the server connection until it is terminated. * to monitor the server connection until it is terminated.
*/ */
public class ConnectionListener extends Thread implements HandshakeCompletedListener public class ConnectionListener extends Thread implements
{ HandshakeCompletedListener {
private Socket _socket; private Socket _socket;
private PrintWriter _writer; private PrintWriter _writer;
private BufferedReader _reader; private BufferedReader _reader;
private Process _serverProcess; private Process _serverProcess;
private String _port; private String _port;
private boolean _done; private boolean _done;
private BufferedReader _outReader; private BufferedReader _outReader;
private BufferedReader _errReader; private BufferedReader _errReader;
/** /**
* Constructor * Constructor
* @param socket a socket to the daemon *
* @param socket
* a socket to the daemon
*/ */
public ConnectionListener(Socket socket) public ConnectionListener(Socket socket) {
{
_socket = socket; _socket = socket;
try try {
{ _writer = new PrintWriter(new OutputStreamWriter(_socket
_writer = new PrintWriter(new OutputStreamWriter(_socket.getOutputStream(), DE.ENCODING_UTF_8)); .getOutputStream(), DE.ENCODING_UTF_8));
_reader = new BufferedReader(new InputStreamReader(_socket.getInputStream(), DE.ENCODING_UTF_8)); _reader = new BufferedReader(new InputStreamReader(_socket
} .getInputStream(), DE.ENCODING_UTF_8));
catch (java.io.IOException e) } catch (java.io.IOException e) {
{
System.out.println("ServerLauncher:" + e); System.out.println("ServerLauncher:" + e);
} }
} }
@ -94,10 +97,8 @@ public class ServerLauncher extends Thread
/** /**
* Called when shutdown * Called when shutdown
*/ */
public void finalize() throws Throwable public void finalize() throws Throwable {
{ if (_serverProcess != null) {
if (_serverProcess != null)
{
_serverProcess.destroy(); _serverProcess.destroy();
} }
super.finalize(); super.finalize();
@ -107,44 +108,33 @@ public class ServerLauncher extends Thread
* Listens to the connection and prints any output while the connection * Listens to the connection and prints any output while the connection
* is active * is active
*/ */
public void run() public void run() {
{
_done = true; _done = true;
if (listen()) if (listen()) {
{ if (_serverProcess != null) {
if (_serverProcess != null)
{
_done = false; _done = false;
try try {
{
String line = null; String line = null;
while ((_outReader != null) && ((line = _outReader.readLine()) != null)) while ((_outReader != null)
{ && ((line = _outReader.readLine()) != null)) {
if (line.equals(ServerReturnCodes.RC_FINISHED)) if (line.equals(ServerReturnCodes.RC_FINISHED)) {
{
break; break;
} } else {
else
{
System.out.println(line); System.out.println(line);
} }
} }
if (_outReader != null) if (_outReader != null) {
{
_outReader.close(); _outReader.close();
} }
if (_errReader != null) if (_errReader != null) {
{
_errReader.close(); _errReader.close();
} }
_serverProcess.waitFor(); _serverProcess.waitFor();
} } catch (Exception e) {
catch (Exception e)
{
System.out.println("ServerLauncher:" + e); System.out.println("ServerLauncher:" + e);
} }
} }
@ -154,34 +144,53 @@ public class ServerLauncher extends Thread
_errReader = null; _errReader = null;
_serverProcess = null; _serverProcess = null;
_done = true; _done = true;
} } else {
else
{
_done = true; _done = true;
} }
} }
/** /**
* Indicates whether the connection has terminated or not * Indicates whether the connection has terminated or not
*
* @return true if the connection has terminated * @return true if the connection has terminated
*/ */
public boolean isDone() public boolean isDone() {
{
return _done; return _done;
} }
/** /**
* Returns the DataStore server port used * Returns the DataStore server port used
*
* @return the server port * @return the server port
*/ */
public String getServerPort() public String getServerPort() {
{
return _port; return _port;
} }
private boolean isPortInRange(String portStr, String portRange) {
if (portRange != null) {
String[] range = portRange.split("-");
if (range.length == 2) {
int lPort = 0;
int hPort = 0;
int port = 0;
try {
lPort = Integer.parseInt(range[0]);
hPort = Integer.parseInt(range[1]);
port = Integer.parseInt(portStr);
} catch (Exception e) {
}
return (port >= lPort && port <= hPort);
}
}
return true;
}
/** /**
* Attempt to start a new DataStore server. The port and the ticket for a * Attempt to start a new DataStore server. The port and the ticket for
* newly started DataStore are captured and sent back to the client so that it * a newly started DataStore are captured and sent back to the client so
* may connect to the server. * that it may connect to the server.
* *
* @return whether the server started successfully * @return whether the server started successfully
*/ */
@ -206,12 +215,26 @@ public class ServerLauncher extends Thread
} }
if (_serverPortRange != null && (_port == null || _port.equals("0"))) if (_serverPortRange != null && (_port == null || _port.equals("0")))
{ {
_port = _serverPortRange; _port = _serverPortRange;
} }
{ {
boolean isError = false;
if (_serverPortRange != null && _port != _serverPortRange)
{
if (!isPortInRange(_port, _serverPortRange))
{
String message = IDataStoreConstants.PORT_OUT_RANGE;
message += _serverPortRange;
_writer.println(message);
isError = true;
}
}
if (!isError)
{
// start new server // start new server
try try
{ {
@ -298,7 +321,8 @@ public class ServerLauncher extends Thread
} }
else else
{ {
// look for the server startup string, it needs to occur somewhere in the line. // look for the server startup string, it needs to occur
// somewhere in the line.
String status = _errReader.readLine(); String status = _errReader.readLine();
while (status!=null && (status.indexOf(ServerReturnCodes.RC_DSTORE_SERVER_MAGIC) < 0)) while (status!=null && (status.indexOf(ServerReturnCodes.RC_DSTORE_SERVER_MAGIC) < 0))
{ {
@ -326,7 +350,8 @@ public class ServerLauncher extends Thread
{ {
status = new String(IDataStoreConstants.UNKNOWN_PROBLEM); status = new String(IDataStoreConstants.UNKNOWN_PROBLEM);
} }
//TODO Make sure that the client doesnt try connecting forever // TODO Make sure that the client doesnt try
// connecting forever
_writer.println(status); _writer.println(status);
_serverProcess.destroy(); _serverProcess.destroy();
@ -338,11 +363,13 @@ public class ServerLauncher extends Thread
_errReader = null; _errReader = null;
} }
} }
} }
catch (IOException e) catch (IOException e)
{ {
_writer.println(IDataStoreConstants.SERVER_FAILURE + e); _writer.println(IDataStoreConstants.SERVER_FAILURE + e);
} }
}
} }
_writer.flush(); _writer.flush();
@ -360,33 +387,31 @@ public class ServerLauncher extends Thread
return connected; return connected;
} }
public void handshakeCompleted(HandshakeCompletedEvent event) {
public void handshakeCompleted(HandshakeCompletedEvent event)
{
System.out.println("handshake completed"); System.out.println("handshake completed");
System.out.println(event); System.out.println(event);
} }
} }
private ServerSocket _serverSocket; private ServerSocket _serverSocket;
private String _path; private String _path;
private ArrayList _connections; private ArrayList _connections;
private String _serverPortRange; private String _serverPortRange;
private ISSLProperties _sslProperties; private ISSLProperties _sslProperties;
public static int DEFAULT_DAEMON_PORT = 4035; public static int DEFAULT_DAEMON_PORT = 4035;
/** /**
* Constructor * Constructor
*/ */
public ServerLauncher() public ServerLauncher() {
{
String pluginPath = System.getProperty("A_PLUGIN_PATH"); String pluginPath = System.getProperty("A_PLUGIN_PATH");
if (pluginPath == null) if (pluginPath == null) {
{
System.out.println("A_PLUGIN_PATH is not defined"); System.out.println("A_PLUGIN_PATH is not defined");
System.exit(-1); System.exit(-1);
} }
@ -400,13 +425,13 @@ public class ServerLauncher extends Thread
/** /**
* Constructor * Constructor
* @param portStr the port for the daemon socket to run on *
* @param portStr
* the port for the daemon socket to run on
*/ */
public ServerLauncher(String portStr) public ServerLauncher(String portStr) {
{
String pluginPath = System.getProperty("A_PLUGIN_PATH"); String pluginPath = System.getProperty("A_PLUGIN_PATH");
if (pluginPath == null) if (pluginPath == null) {
{
System.out.println("A_PLUGIN_PATH is not defined"); System.out.println("A_PLUGIN_PATH is not defined");
System.exit(-1); System.exit(-1);
} }
@ -416,18 +441,18 @@ public class ServerLauncher extends Thread
_connections = new ArrayList(); _connections = new ArrayList();
init(portStr); init(portStr);
} }
/** /**
* Constructor * Constructor
* @param portStr the port for the daemon socket to run on *
* @param serverPortRange the port range for launched servers * @param portStr
* the port for the daemon socket to run on
* @param serverPortRange
* the port range for launched servers
*/ */
public ServerLauncher(String portStr, String serverPortRange) public ServerLauncher(String portStr, String serverPortRange) {
{
String pluginPath = System.getProperty("A_PLUGIN_PATH"); String pluginPath = System.getProperty("A_PLUGIN_PATH");
if (pluginPath == null) if (pluginPath == null) {
{
System.out.println("A_PLUGIN_PATH is not defined"); System.out.println("A_PLUGIN_PATH is not defined");
System.exit(-1); System.exit(-1);
} }
@ -439,128 +464,107 @@ public class ServerLauncher extends Thread
init(portStr); init(portStr);
} }
private String getKeyStoreLocation() private String getKeyStoreLocation() {
{
return _sslProperties.getDaemonKeyStorePath(); return _sslProperties.getDaemonKeyStorePath();
} }
private String getKeyStorePassword() private String getKeyStorePassword() {
{
return _sslProperties.getDaemonKeyStorePassword(); return _sslProperties.getDaemonKeyStorePassword();
} }
/** /**
* initializes the DataStore daemon * initializes the DataStore daemon
* *
* @param port the daemon port * @param port
* the daemon port
*/ */
public void init(String portStr) public void init(String portStr) {
{
// create server socket from port // create server socket from port
_sslProperties = new ServerSSLProperties(); _sslProperties = new ServerSSLProperties();
// determine if portStr is a port range or just a port // determine if portStr is a port range or just a port
String[] range = portStr.split("-"); String[] range = portStr.split("-");
if (range.length == 2) if (range.length == 2) {
{
int lPort = 0; int lPort = 0;
int hPort = 0; int hPort = 0;
try try {
{
lPort = Integer.parseInt(range[0]); lPort = Integer.parseInt(range[0]);
hPort = Integer.parseInt(range[1]); hPort = Integer.parseInt(range[1]);
} catch (Exception e) {
} }
catch (Exception e)
{
}
boolean socketBound = false; boolean socketBound = false;
for (int i = lPort; i < hPort && !socketBound; i++) for (int i = lPort; i < hPort && !socketBound; i++) {
{
// create server socket from port // create server socket from port
try try {
{ if (_sslProperties.usingSSL()) {
if (_sslProperties.usingSSL())
{
String keyStoreFileName = getKeyStoreLocation(); String keyStoreFileName = getKeyStoreLocation();
String keyStorePassword = getKeyStorePassword(); String keyStorePassword = getKeyStorePassword();
try try {
{ SSLContext sslContext = DStoreSSLContext
SSLContext sslContext = DStoreSSLContext.getServerSSLContext(keyStoreFileName, keyStorePassword); .getServerSSLContext(keyStoreFileName,
keyStorePassword);
_serverSocket = sslContext.getServerSocketFactory().createServerSocket(i); _serverSocket = sslContext.getServerSocketFactory()
} .createServerSocket(i);
catch (Exception e) } catch (Exception e) {
{
e.printStackTrace(); e.printStackTrace();
} }
} } else {
else
{
_serverSocket = new ServerSocket(i); _serverSocket = new ServerSocket(i);
} }
if (_serverSocket != null && _serverSocket.getLocalPort() > 0) if (_serverSocket != null
{ && _serverSocket.getLocalPort() > 0) {
socketBound = true; socketBound = true;
System.out.println("Daemon running on: " + InetAddress.getLocalHost().getHostName() + ", port: " + i); System.out.println("Daemon running on: "
+ InetAddress.getLocalHost().getHostName()
+ ", port: " + i);
} }
} } catch (UnknownHostException e) {
catch (UnknownHostException e) System.err
{ .println("Networking problem, can't resolve local host");
System.err.println("Networking problem, can't resolve local host");
e.printStackTrace(); e.printStackTrace();
System.exit(-1); System.exit(-1);
} } catch (BindException e) {
catch (BindException e) System.err.println("socket taken on " + i);
{
System.err.println("socket taken on "+i);
// keep going // keep going
} } catch (IOException e) {
catch (IOException e)
{
System.err.println("Failure to create ServerSocket"); System.err.println("Failure to create ServerSocket");
e.printStackTrace(); e.printStackTrace();
System.exit(-1); System.exit(-1);
} }
} }
} } else {
else
{
int port = Integer.parseInt(portStr); int port = Integer.parseInt(portStr);
try try {
{ if (_sslProperties.usingSSL()) {
if (_sslProperties.usingSSL())
{
String keyStoreFileName = getKeyStoreLocation(); String keyStoreFileName = getKeyStoreLocation();
String keyStorePassword = getKeyStorePassword(); String keyStorePassword = getKeyStorePassword();
try try {
{ SSLContext sslContext = DStoreSSLContext
SSLContext sslContext = DStoreSSLContext.getServerSSLContext(keyStoreFileName, keyStorePassword); .getServerSSLContext(keyStoreFileName,
keyStorePassword);
_serverSocket = sslContext.getServerSocketFactory().createServerSocket(port); _serverSocket = sslContext.getServerSocketFactory()
} .createServerSocket(port);
catch (Exception e) } catch (Exception e) {
{
e.printStackTrace(); e.printStackTrace();
} }
} } else {
else
{
_serverSocket = new ServerSocket(port); _serverSocket = new ServerSocket(port);
} }
System.out.println("Daemon running on: " + InetAddress.getLocalHost().getHostName() + ", port: " + port); System.out.println("Daemon running on: "
} + InetAddress.getLocalHost().getHostName() + ", port: "
catch (UnknownHostException e) + port);
{ } catch (UnknownHostException e) {
System.err.println("Networking problem, can't resolve local host"); System.err
.println("Networking problem, can't resolve local host");
e.printStackTrace(); e.printStackTrace();
System.exit(-1); System.exit(-1);
} } catch (IOException e) {
catch (IOException e)
{
System.err.println("Failure to create ServerSocket"); System.err.println("Failure to create ServerSocket");
e.printStackTrace(); e.printStackTrace();
System.exit(-1); System.exit(-1);
@ -570,16 +574,16 @@ public class ServerLauncher extends Thread
/** /**
* Return the connection listener for the specified port if there is one * Return the connection listener for the specified port if there is one
* @param port the port *
* @param port
* the port
* @return the listener associated with the port * @return the listener associated with the port
*/ */
protected ConnectionListener getListenerForPort(String port) protected ConnectionListener getListenerForPort(String port) {
{ for (int i = 0; i < _connections.size(); i++) {
for (int i = 0; i < _connections.size(); i++) ConnectionListener listener = (ConnectionListener) _connections
{ .get(i);
ConnectionListener listener = (ConnectionListener) _connections.get(i); if (listener.getServerPort().equals(port)) {
if (listener.getServerPort().equals(port))
{
return listener; return listener;
} }
} }
@ -587,50 +591,41 @@ public class ServerLauncher extends Thread
return null; return null;
} }
/** /**
* Run the daemon * Run the daemon
*/ */
public void run() public void run() {
{ while (true) {
while (true) try {
{
try
{
boolean connectionOkay = true; boolean connectionOkay = true;
Socket newSocket = _serverSocket.accept(); Socket newSocket = _serverSocket.accept();
if (_sslProperties.usingSSL()) if (_sslProperties.usingSSL()) {
{
SSLSocket sslSocket = (SSLSocket) newSocket; SSLSocket sslSocket = (SSLSocket) newSocket;
sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() sslSocket
{ .addHandshakeCompletedListener(new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) public void handshakeCompleted(
{ HandshakeCompletedEvent event) {
System.out.println("handshake completed"); System.out.println("handshake completed");
} }
}); });
SSLSession session = sslSocket.getSession(); SSLSession session = sslSocket.getSession();
if (session == null) if (session == null) {
{
System.out.println("handshake failed"); System.out.println("handshake failed");
sslSocket.close(); sslSocket.close();
connectionOkay = false; connectionOkay = false;
} }
} }
if (connectionOkay) if (connectionOkay) {
{ ConnectionListener listener = new ConnectionListener(
ConnectionListener listener = new ConnectionListener(newSocket); newSocket);
listener.start(); listener.start();
_connections.add(listener); _connections.add(listener);
} }
} } catch (IOException ioe) {
catch (IOException ioe)
{
System.err.println("Server: error initializing socket: " + ioe); System.err.println("Server: error initializing socket: " + ioe);
System.exit(-1); System.exit(-1);
} }
@ -639,24 +634,21 @@ public class ServerLauncher extends Thread
/** /**
* Entry point into the DataStore daemon * Entry point into the DataStore daemon
* *
* @param args the port for the daemon to run on (default is 4035). Optionally, the second arg specifies whether to use SSL or not. * @param args
* an optional second arg can be used to specify the port range of servers that get launched * the port for the daemon to run on (default is 4035).
* Optionally, the second arg specifies whether to use SSL or
* not. an optional second arg can be used to specify the port
* range of servers that get launched
*/ */
public static void main(String args[]) public static void main(String args[]) {
{ if (args.length == 2) {
if (args.length == 2)
{
ServerLauncher theServer = new ServerLauncher(args[0], args[1]); ServerLauncher theServer = new ServerLauncher(args[0], args[1]);
theServer.start(); theServer.start();
} } else if (args.length == 1) {
else if (args.length == 1)
{
ServerLauncher theServer = new ServerLauncher(args[0]); ServerLauncher theServer = new ServerLauncher(args[0]);
theServer.start(); theServer.start();
} } else {
else
{
ServerLauncher theServer = new ServerLauncher(); ServerLauncher theServer = new ServerLauncher();
theServer.start(); theServer.start();
} }

View file

@ -623,6 +623,19 @@ public class DStoreConnectorService extends AbstractConnectorService implements
return; return;
} }
} }
else if (isPortOutOfRange(launchMsg))
{
launchFailed = true;
SystemMessage cmsg = RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_PORT_OUT_RANGE);
int colonIndex = launchMsg.indexOf(':');
String portRange = launchMsg.substring(colonIndex + 1);
cmsg.makeSubstitution(portRange);
ShowConnectMessage msgAction = new ShowConnectMessage(cmsg);
Display.getDefault().asyncExec(msgAction);
return;
}
else else
{ {
launchFailed = true; launchFailed = true;
@ -1057,6 +1070,11 @@ public class DStoreConnectorService extends AbstractConnectorService implements
} }
} }
protected boolean isPortOutOfRange(String message)
{
return message.indexOf(IDataStoreConstants.PORT_OUT_RANGE) != -1;
}
protected boolean isPasswordExpired(String message) protected boolean isPasswordExpired(String message)
{ {
return message.indexOf(IDataStoreConstants.PASSWORD_EXPIRED) != -1; return message.indexOf(IDataStoreConstants.PASSWORD_EXPIRED) != -1;

View file

@ -122,6 +122,7 @@ public interface ISystemMessages
public static final String MSG_STARTING_SERVER_VIA_DAEMON = "RSEC2311"; public static final String MSG_STARTING_SERVER_VIA_DAEMON = "RSEC2311";
public static final String MSG_CONNECTING_TO_SERVER= "RSEC2312"; public static final String MSG_CONNECTING_TO_SERVER= "RSEC2312";
public static final String MSG_INITIALIZING_SERVER= "RSEC2313"; public static final String MSG_INITIALIZING_SERVER= "RSEC2313";
public static final String MSG_PORT_OUT_RANGE = "RSEC2316";
//public static final String MSG_DISCONNECT_PREFIX = MSG_PREFIX + "Disconnect."; //public static final String MSG_DISCONNECT_PREFIX = MSG_PREFIX + "Disconnect.";
public static final String MSG_DISCONNECT_PROGRESS = "RSEG1059"; //MSG_DISCONNECT_PREFIX + "Disconnecting"; public static final String MSG_DISCONNECT_PROGRESS = "RSEG1059"; //MSG_DISCONNECT_PREFIX + "Disconnecting";

View file

@ -1356,6 +1356,7 @@ Contributors:
<LevelOne>Initializing Remote Systems Explorer host server</LevelOne> <LevelOne>Initializing Remote Systems Explorer host server</LevelOne>
<LevelTwo></LevelTwo> <LevelTwo></LevelTwo>
</Message> </Message>
<Message ID="2314" Indicator="I"> <Message ID="2314" Indicator="I">
<LevelOne>Communication with the remote system %1 has been secured using SSL. Continue?</LevelOne> <LevelOne>Communication with the remote system %1 has been secured using SSL. Continue?</LevelOne>
<LevelTwo></LevelTwo> <LevelTwo></LevelTwo>
@ -1364,7 +1365,10 @@ Contributors:
<LevelOne>Connection %1 has not been secured using SSL. Proceed anyways?</LevelOne> <LevelOne>Connection %1 has not been secured using SSL. Proceed anyways?</LevelOne>
<LevelTwo></LevelTwo> <LevelTwo></LevelTwo>
</Message> </Message>
<Message ID="2316" Indicator="E">
<LevelOne>The specified server port is out of the allowed range: %1</LevelOne>
<LevelTwo></LevelTwo>
</Message>
<Message ID="3001" Indicator="E"> <Message ID="3001" Indicator="E">
<LevelOne>The connection %1 is currently offline and cannot be connected.</LevelOne> <LevelOne>The connection %1 is currently offline and cannot be connected.</LevelOne>
<LevelTwo>You have performed an action which requires a live connection to the remote system. Switch the connection to online and perform the action again. To switch the connection online, right click on the connection %1 and select the Work Offline action.</LevelTwo> <LevelTwo>You have performed an action which requires a live connection to the remote system. Switch the connection to online and perform the action again. To switch the connection online, right click on the connection %1 and select the Work Offline action.</LevelTwo>