### Eclipse Workspace Patch 1.0 #P org.eclipse.ui.ide.application Index: META-INF/MANIFEST.MF =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.ide.application/META-INF/MANIFEST.MF,v retrieving revision 1.8 diff -u -r1.8 MANIFEST.MF --- META-INF/MANIFEST.MF 1 May 2008 14:01:17 -0000 1.8 +++ META-INF/MANIFEST.MF 2 Jul 2008 21:03:53 -0000 @@ -13,7 +13,8 @@ org.eclipse.ui.navigator.resources;bundle-version="[3.2.100,4.0.0)", org.eclipse.core.net;bundle-version="[1.0.0,2.0.0)", org.eclipse.update.core;bundle-version="[3.1.100,4.0.0)", - com.ibm.icu;bundle-version="3.8.1" + com.ibm.icu;bundle-version="3.8.1", + org.eclipse.core.filesystem;bundle-version="1.2.0" Export-Package: org.eclipse.ui.internal.ide.application;x-internal:=true, org.eclipse.ui.internal.ide.application.dialogs;x-internal:=true Bundle-RequiredExecutionEnvironment: J2SE-1.4 Index: src/org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java,v retrieving revision 1.13 diff -u -r1.13 IDEWorkbenchAdvisor.java --- src/org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java 1 May 2008 14:01:17 -0000 1.13 +++ src/org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java 2 Jul 2008 21:03:53 -0000 @@ -16,7 +16,7 @@ import java.util.Iterator; import java.util.Map; import java.util.TreeMap; - +import org.eclipse.core.filesystem.EFS; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; @@ -27,6 +27,7 @@ import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IBundleGroup; import org.eclipse.core.runtime.IBundleGroupProvider; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; @@ -46,6 +47,9 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Listener; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.application.IWorkbenchConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; @@ -128,7 +132,7 @@ private IDEIdleHelper idleHelper; private Listener settingsChangeListener; - + /** * Support class for monitoring workspace changes and periodically * validating the undo history @@ -140,6 +144,10 @@ */ private AbstractStatusHandler ideWorkbenchErrorHandler; + static IDEWorkbenchAdvisor getInstance() { + return workbenchAdvisor; + } + /** * Creates a new workbench advisor instance. */ @@ -159,7 +167,7 @@ public void initialize(IWorkbenchConfigurer configurer) { PluginActionBuilder.setAllowIdeLogging(true); - + // make sure we always save and restore workspace state configurer.setSaveAndRestore(true); @@ -194,7 +202,7 @@ // initialize idle handler idleHelper = new IDEIdleHelper(configurer); - + // initialize the workspace undo monitor workspaceUndoMonitor = WorkspaceUndoMonitor.getInstance(); @@ -242,12 +250,51 @@ initializeSettingsChangeListener(); Display.getCurrent().addListener(SWT.Settings, settingsChangeListener); + runStartupCommand(Platform.getCommandLineArgs()); } finally {// Resume background jobs after we startup Job.getJobManager().resume(); } } /** + * Runs a command specified on the command line, if any + */ + boolean runStartupCommand(String[] arguments) { + if (arguments.length == 0) + return false; + // if the last command line argument is a file, open that file in an + // editor + final IPath path = new Path(arguments[arguments.length - 1]); + if (!path.toFile().exists()) + return false; + IWorkbenchWindow window = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow(); + if (window == null) { + IWorkbenchWindow[] windows = PlatformUI.getWorkbench() + .getWorkbenchWindows(); + if (windows.length > 0) + window = windows[0]; + } + if (window == null) + return false; + final IWorkbenchPage page = window.getActivePage(); + if (page == null) + return false; + window.getShell().getDisplay().syncExec(new Runnable() { + public void run() { + try { + IDE.openEditorOnFileStore(page, EFS.getLocalFileSystem() + .getStore(path)); + } catch (PartInitException e) { + IDEWorkbenchPlugin.log( + "Error opening editor on path: " + path, e); //$NON-NLS-1$ + } + } + }); + return true; + } + + /** * Activate the proxy service by obtaining it. */ private void activateProxyService() { @@ -260,7 +307,7 @@ } if (proxyService == null) { IDEWorkbenchPlugin.log("Proxy service could not be found."); //$NON-NLS-1$ - } + } } /** @@ -697,7 +744,7 @@ declareWorkbenchImage(ideBundle, IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY, PATH_ETOOL + "problem_category.gif", true); //$NON-NLS-1$ - + // synchronization indicator objects // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_WBET_STAT, // PATH_OVERLAY+"wbet_stat.gif"); Index: src/org/eclipse/ui/internal/ide/application/IDEApplication.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java,v retrieving revision 1.7 diff -u -r1.7 IDEApplication.java --- src/org/eclipse/ui/internal/ide/application/IDEApplication.java 3 Jun 2008 17:56:02 -0000 1.7 +++ src/org/eclipse/ui/internal/ide/application/IDEApplication.java 2 Jul 2008 21:03:53 -0000 @@ -105,7 +105,7 @@ Platform.endSplash(); return EXIT_OK; } - + // create the workbench with this advisor and run it until it exits // N.B. createWorkbench remembers the advisor, and also registers // the workbench globally so that all UI plug-ins can find it using @@ -217,6 +217,15 @@ // -data @noDefault or -data not specified, prompt and set ChooseWorkspaceData launchData = new ChooseWorkspaceData(instanceLoc .getDefault()); + + //check if someone is listening that we can pass our command line to + if (launchData.getPort() != 0) { + if (new IDECommunication().writeToPort(launchData.getPort(), launchData.getIPCAuthorization())) { + //we successfully punted this application's command line to another instance, + //so we can just shutdown this instance. + return false; + } + } boolean force = false; while (true) { @@ -233,6 +242,11 @@ // the operation will fail if the url is not a valid // instance data area, so other checking is unneeded if (instanceLoc.setURL(workspaceUrl, true)) { + int[] ipcData = new IDECommunication().listen(); + if (ipcData != null) { + launchData.setPort(ipcData[0]); + launchData.setIPCAuthorization(ipcData[1]); + } launchData.writePersistedData(); writeWorkspaceVersion(); return true; Index: src/org/eclipse/ui/internal/ide/application/IDECommunication.java =================================================================== RCS file: src/org/eclipse/ui/internal/ide/application/IDECommunication.java diff -N src/org/eclipse/ui/internal/ide/application/IDECommunication.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/ui/internal/ide/application/IDECommunication.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2008 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.ui.internal.ide.application; + +import java.security.SecureRandom; + +import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; + +import java.io.*; +import java.net.*; +import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.Job; + +/** + * A helper class that deals with interprocess communication between applications. + *
+ * This class uses a socket connection on a random available port to establish
+ * communication between applications. The client must provide an authorization
+ * code to protect against communication from arbitrary port sniffing applications.
+ *
+ * @since 3.5
+ */
+class IDECommunication {
+ /**
+ * A job that listens on a socket for a command, and then invokes the
+ * workbench advisor to handle the command.
+ */
+ class ListenJob extends Job {
+ private ServerSocket server;
+ private int auth;
+
+ public ListenJob(ServerSocket server, int auth) {
+ super("Listen"); //$NON-NLS-1$
+ this.server = server;
+ this.auth = auth;
+ setSystem(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#canceling()
+ */
+ protected void canceling() {
+ try {
+ server.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected IStatus run(IProgressMonitor monitor) {
+ if (server.isClosed())
+ return Status.OK_STATUS;
+ Socket client = null;
+ DataInputStream in = null;
+ DataOutputStream out = null;
+ try {
+ client = server.accept();
+ in = new DataInputStream(client.getInputStream());
+ out = new DataOutputStream(client.getOutputStream());
+ //read the protocol version
+ int version = in.readInt();
+ String[] args = null;
+ if (version == PROTOCOL_VERSION_ONE) {
+ //read the authorization checksum
+ int receivedAuth = in.readInt();
+ //abort immediately if we failed to read the authorization integer
+ if (receivedAuth != auth) {
+ schedule(SCHEDULE_DELAY);
+ return new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, "Connection from unauthorized client on port " + server.getLocalPort()); //$NON-NLS-1$
+ }
+ args = new String[in.readInt()];
+ for (int i = 0; i < args.length; i++) {
+ args[i] = in.readUTF();
+ }
+ }
+ //pass the read arguments to the workbench advisor
+ IDEWorkbenchAdvisor advisor = IDEWorkbenchAdvisor.getInstance();
+ if (advisor != null && args != null) {
+ if (advisor.runStartupCommand(args))
+ out.writeUTF(OK);
+ else
+ out.writeUTF(FAIL);
+ }
+ } catch (IOException e) {
+ IDEWorkbenchPlugin.log("Error communicating with client", e); //$NON-NLS-1$
+ } finally {
+ safeClose(in);
+ safeClose(out);
+ safeClose(client);
+ }
+ //reschedule
+ schedule(SCHEDULE_DELAY);
+ return Status.OK_STATUS;
+ }
+ }
+
+ /**
+ * The first communication protocol version. This version contains an authorization
+ * integer followed by a list of command line arguments. The format is: int authorizationId, int N, String[N].
+ */
+ private static final int PROTOCOL_VERSION_ONE = 1;
+
+ private static final long SCHEDULE_DELAY = 1000;
+
+ static final String OK = "OK"; //$NON-NLS-1$
+ static final String FAIL = "FAIL"; //$NON-NLS-1$
+
+ /**
+ * Start listening for commands.
+ * @return An integer array of size two. The first integer is the port
+ * number we are listening on, and the second integer is an authorization
+ * integer used to validate connections. Returns null
if
+ * there was a failure to establish communication
+ */
+ public int[] listen() {
+ try {
+ ServerSocket server = new ServerSocket(0);
+ int auth = new SecureRandom().nextInt();
+ new ListenJob(server, auth).schedule(SCHEDULE_DELAY);
+ return new int[] {server.getLocalPort(), auth};
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Close the given stream and ignore secondary failures.
+ */
+ void safeClose(DataInputStream in) {
+ try {
+ if (in != null)
+ in.close();
+ } catch (IOException e) {
+ //ignore secondary failure while closing
+ }
+ }
+
+ /**
+ * Close the given stream and ignore secondary failures.
+ */
+ void safeClose(DataOutputStream out) {
+ try {
+ if (out != null)
+ out.close();
+ } catch (IOException e) {
+ //ignore secondary failure while closing
+ }
+ }
+
+ /**
+ * Close the given socket and ignore secondary failures.
+ */
+ void safeClose(Socket socket) {
+ try {
+ if (socket != null)
+ socket.close();
+ } catch (IOException e) {
+ //ignore secondary failure while closing
+ }
+ }
+
+ /**
+ * Writes this application's command line arguments to the given port.
+ *
+ * @param port The port to write data to
+ * @param auth The authorization integer
+ * @return true
if written successfully, and false
otherwise.
+ */
+ public boolean writeToPort(int port, int auth) {
+ Socket socket = null;
+ DataOutputStream out = null;
+ DataInputStream in = null;
+ try {
+ socket = new Socket("localhost", port);//$NON-NLS-1$
+ out = new DataOutputStream(socket.getOutputStream());
+ in = new DataInputStream(socket.getInputStream());
+ out.writeInt(PROTOCOL_VERSION_ONE);
+ out.writeInt(auth);
+ String[] args = Platform.getCommandLineArgs();
+ out.writeInt(args.length);
+ for (int i = 0; i < args.length; i++)
+ out.writeUTF(args[i]);
+ //server will return OK or FAIL
+ return in.readUTF().equals(OK);
+ } catch (UnknownHostException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ } finally {
+ safeClose(socket);
+ safeClose(out);
+ safeClose(in);
+ }
+ }
+
+}
#P org.eclipse.ui.ide
Index: src/org/eclipse/ui/internal/ide/ChooseWorkspaceData.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/ChooseWorkspaceData.java,v
retrieving revision 1.21
diff -u -r1.21 ChooseWorkspaceData.java
--- src/org/eclipse/ui/internal/ide/ChooseWorkspaceData.java 24 Mar 2008 19:13:35 -0000 1.21
+++ src/org/eclipse/ui/internal/ide/ChooseWorkspaceData.java 2 Jul 2008 21:03:54 -0000
@@ -69,7 +69,7 @@
/**
* This is the second version of the encode/decode protocol that uses the
* confi area preferences store for persistence. This version is the same as
- * the previous version except it uses a \n character to seperate the path
+ * the previous version except it uses a \n character to separate the path
* entries instead of commas. (see bug 98467)
*
* @since 3.3.1
@@ -83,6 +83,18 @@
private String selection;
private String[] recentWorkspaces;
+
+ /**
+ * The port on which an instance of this configuration is already listening for remote communication.
+ * This value is 0 if nobody is already listening.
+ */
+ private int port = 0;
+ /**
+ * An authorization integer that a client must provide when communication with
+ * this application. This is a simple security measure to prevent a port scanner
+ * from executing commands in this application.
+ */
+ private int ipcAuth = 0;
// xml tags
private static interface XML {
@@ -101,7 +113,11 @@
public static final String MAX_LENGTH = "maxLength"; //$NON-NLS-1$
public static final String PATH = "path"; //$NON-NLS-1$
- }
+
+ public static final String PORT= "port"; //$NON-NLS-1$
+
+ public static final String IPC_AUTH= "ipcAuth"; //$NON-NLS-1$
+}
/**
* Creates a new instance, loading persistent data if its found.
@@ -151,6 +167,23 @@
}
initialDefault = dir;
}
+
+ /**
+ * Sets the port on which an instance is listening for remote communication.
+ * @param port The port, or 0 to indicate not listening.
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * Sets an authorization integer that a client must provide to establish
+ * remote communication. This is used to prevent a port sniffer from
+ * executing commands.
+ */
+ public void setIPCAuthorization(int auth) {
+ this.ipcAuth = auth;
+ }
/**
* Return the currently selected workspace or null if nothing is selected.
@@ -173,6 +206,23 @@
public String[] getRecentWorkspaces() {
return recentWorkspaces;
}
+
+ /**
+ * Returns the port on which an instance of this same configuration is
+ * already listening for remote communication, or 0 if nobody is listening.
+ * @return The port number, or 0.
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns the authorization integer for establishing interprocess communication.
+ * @return The authorization integer
+ */
+ public int getIPCAuthorization() {
+ return ipcAuth;
+ }
/**
* The argument workspace has been selected, update the receiver. Does not
@@ -230,8 +280,14 @@
// 5. store the protocol version used to encode the list
node.putInt(IDE.Preferences.RECENT_WORKSPACES_PROTOCOL,
PERS_ENCODING_VERSION_CONFIG_PREFS_NO_COMMAS);
+
+ // 6. store the interprocess communication data, if any
+ if (port != 0) {
+ node.putInt(XML.PORT, port);
+ node.putInt(XML.IPC_AUTH, ipcAuth);
+ }
- // 6. store the node
+ // 7. store the node
try {
node.flush();
} catch (BackingStoreException e) {
@@ -405,6 +461,10 @@
.getString(IDE.Preferences.RECENT_WORKSPACES);
recentWorkspaces = decodeStoredWorkspacePaths(protocol, max, workspacePathPref);
+ // 5. load the interprocess communication data
+ port = store.getInt(XML.PORT);
+ ipcAuth = store.getInt(XML.IPC_AUTH);
+
return true;
}