diff options
author | slewis | 2014-10-14 20:11:49 +0000 |
---|---|---|
committer | slewis | 2014-10-14 20:11:49 +0000 |
commit | 194b6887237a00b6be8c4614babffa4fce6cc18c (patch) | |
tree | 1dc02ec2ce76dda3e93e5f4702befb9fc2278cd0 | |
parent | d42f18de0bd535101a7866edb94f58bba5a2819b (diff) | |
download | org.eclipse.ecf-194b6887237a00b6be8c4614babffa4fce6cc18c.tar.gz org.eclipse.ecf-194b6887237a00b6be8c4614babffa4fce6cc18c.tar.xz org.eclipse.ecf-194b6887237a00b6be8c4614babffa4fce6cc18c.zip |
Changes associated with bug
https://bugs.eclipse.org/bugs/show_bug.cgi?id=445729
As per comment https://bugs.eclipse.org/bugs/show_bug.cgi?id=445729#c3
these changes were originally submitted by J Langley via gerrit
https://git.eclipse.org/r/#/c/34754/
Small changes needed to be made to the original submission, and he and I
apparently did not do the right thing to make those changes via gerrit.
So I applied these changes to master directly.
Change-Id: I286fe2325ee9fd724067b4281882eb39fffdd440
5 files changed, 445 insertions, 1 deletions
diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/META-INF/MANIFEST.MF b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/META-INF/MANIFEST.MF index d1f06ee60..96c59dbb0 100644 --- a/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/META-INF/MANIFEST.MF +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin.name Bundle-SymbolicName: org.eclipse.ecf.provider.filetransfer.scp;singleton:=true -Bundle-Version: 2.0.100.qualifier +Bundle-Version: 2.1.0.qualifier Bundle-Activator: org.eclipse.ecf.internal.provider.filetransfer.scp.Activator Bundle-Vendor: %plugin.provider Import-Package: com.jcraft.jsch, diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/plugin.xml b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/plugin.xml index db397a12d..3ce5bd334 100644 --- a/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/plugin.xml +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/plugin.xml @@ -15,4 +15,12 @@ protocol="scp"> </retrieveFileTransferProtocolFactory> </extension> + <extension + point="org.eclipse.ecf.provider.filetransfer.browseFileTransferProtocolFactory"> + <browseFileTransferProtocolFactory + class="org.eclipse.ecf.internal.provider.filetransfer.scp.ScpBrowseFileTransferFactory" + priority="45" + protocol="scp"> + </browseFileTransferProtocolFactory> + </extension> </plugin> diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/src/org/eclipse/ecf/internal/provider/filetransfer/scp/ScpBrowseFileTransferFactory.java b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/src/org/eclipse/ecf/internal/provider/filetransfer/scp/ScpBrowseFileTransferFactory.java new file mode 100644 index 000000000..7cabdb651 --- /dev/null +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/src/org/eclipse/ecf/internal/provider/filetransfer/scp/ScpBrowseFileTransferFactory.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2014 CohesionForce Inc + * 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: + * CohesionForce Inc - initial API and implementation + *******************************************************************************/ + +package org.eclipse.ecf.internal.provider.filetransfer.scp; + +import java.net.MalformedURLException; +import java.net.URL; +import org.eclipse.core.runtime.Assert; +import org.eclipse.ecf.core.identity.IDFactory; +import org.eclipse.ecf.core.identity.Namespace; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.core.util.Proxy; +import org.eclipse.ecf.filetransfer.*; +import org.eclipse.ecf.filetransfer.identity.IFileID; +import org.eclipse.ecf.filetransfer.service.IRemoteFileSystemBrowser; +import org.eclipse.ecf.filetransfer.service.IRemoteFileSystemBrowserFactory; +import org.eclipse.ecf.provider.filetransfer.identity.FileTransferNamespace; +import org.eclipse.osgi.util.NLS; + +/** + * Factory for creating a remote file browser. This class is registered in the + * plugin to handle the "scp://" protocol. + * + */ +public class ScpBrowseFileTransferFactory implements + IRemoteFileSystemBrowserFactory { + + public IRemoteFileSystemBrowser newInstance() { + return new IRemoteFileSystemBrowser() { + + private Proxy proxy; + private IConnectContext connectContext; + + public Namespace getBrowseNamespace() { + return IDFactory.getDefault().getNamespaceByName( + FileTransferNamespace.PROTOCOL); + } + + public IRemoteFileSystemRequest sendBrowseRequest( + IFileID directoryOrFileId, + IRemoteFileSystemListener listener) + throws RemoteFileSystemException { + + Assert.isNotNull(directoryOrFileId); + Assert.isNotNull(listener); + URL url; + try { + url = directoryOrFileId.getURL(); + } catch (final MalformedURLException e) { + throw new RemoteFileSystemException( + NLS.bind( + "Exception creating URL for {0}", directoryOrFileId)); //$NON-NLS-1$ + } + + ScpFileSystemBrowser browser = new ScpFileSystemBrowser( + directoryOrFileId, listener, url, connectContext, proxy); + return browser.sendBrowseRequest(); + } + + public void setConnectContextForAuthentication( + IConnectContext connectContext) { + this.connectContext = connectContext; + } + + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } + + public Object getAdapter(Class adapter) { + return null; + } + }; + } +} diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/src/org/eclipse/ecf/internal/provider/filetransfer/scp/ScpFileSystemBrowser.java b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/src/org/eclipse/ecf/internal/provider/filetransfer/scp/ScpFileSystemBrowser.java new file mode 100644 index 000000000..9334ea32e --- /dev/null +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer.scp/src/org/eclipse/ecf/internal/provider/filetransfer/scp/ScpFileSystemBrowser.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2014 CohesionForce Inc + * 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: + * CohesionForce Inc - initial API and implementation + *******************************************************************************/ + +package org.eclipse.ecf.internal.provider.filetransfer.scp; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.Session; +import java.io.*; +import java.net.URL; +import java.util.ArrayList; +import java.util.Map; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ecf.core.identity.IDFactory; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.core.util.Proxy; +import org.eclipse.ecf.filetransfer.IRemoteFile; +import org.eclipse.ecf.filetransfer.IRemoteFileSystemListener; +import org.eclipse.ecf.filetransfer.identity.*; +import org.eclipse.ecf.provider.filetransfer.browse.AbstractFileSystemBrowser; +import org.eclipse.ecf.provider.filetransfer.browse.URLRemoteFile; +import org.eclipse.ecf.provider.filetransfer.identity.FileTransferNamespace; + +/** + * The ScpFileSystemBrowser uses the JCraft JSch package to run remote commands + * using SSH to list files in a directory. + * + */ +public class ScpFileSystemBrowser extends AbstractFileSystemBrowser implements + IScpFileTransfer { + + protected InputStream inputStream; + + protected OutputStream outputStream; + + protected ScpUtil scpUtil; + + protected ChannelExec channel; + protected String username; + + protected static final String SCP_EXEC = System.getProperty( + "org.eclipse.ecf.filetransfer.scp.filebrowse.exec", "exec"); //$NON-NLS-1$ + protected static final String LS_START_COMMAND = System.getProperty( + "org.eclipse.ecf.filetransfer.scp.filebrowse.lscommand.start", + "for file in "); //$NON-NLS-1$; //$NON-NLS-1$ + protected static final String LS_END_COMMAND = System.getProperty( + "org.eclipse.ecf.filetransfer.scp.filebrowse.lscommand.end", + "/*; do stat --format='%F|%s|%Y|%n' $file; done "); //$NON-NLS-1$; //$NON-NLS-1$ + + /** + * Constructor for creating a ScpFileSystemBrowser. + * + * @param directoryOrFileID + * - ID of the remote location to browse + * @param listener + * - will be called asynchronously with events resulting from + * file browsing. + * @param url + * - URL of the parent directory to browse. + * @param connectContext + * - contains username/password to use for the ssh connection + * @param proxy + * - proxy to be used if set. + */ + public ScpFileSystemBrowser(IFileID directoryOrFileID, + IRemoteFileSystemListener listener, URL url, + IConnectContext connectContext, Proxy proxy) { + super(directoryOrFileID, listener, url, connectContext, proxy); + username = directoryOrFile.getUserInfo(); + } + + /** + * Method called from super class to build the list of remote files. + */ + protected void runRequest() throws Exception { + try { + + scpUtil = new ScpUtil(this); + final Session s = scpUtil.getSession(); + s.connect(); + if (s.isConnected()) { + final String targetFileName = scpUtil + .trimTargetFile(directoryOrFile.getPath()); + final String command = LS_START_COMMAND + targetFileName + + LS_END_COMMAND; + channel = (ChannelExec) s.openChannel(SCP_EXEC); + channel.setCommand(command); + final OutputStream outs = channel.getOutputStream(); + inputStream = channel.getInputStream(); + channel.connect(); + + setOutputStream(outs); + + BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream)); + String line = reader.readLine(); + ArrayList strings = new ArrayList(); + while (line != null) { + strings.add(line); + line = reader.readLine(); + } + remoteFiles = new IRemoteFile[strings.size()]; + for (int i = 0; i < strings.size(); i++) { + remoteFiles[i] = createRemoteFile((String) strings.get(i)); + } + } + } catch (final Exception e) { + Activator.getDefault().log( + new Status(IStatus.ERROR, Activator.PLUGIN_ID, + IStatus.ERROR, "runRequest", e)); //$NON-NLS-1$ + } + } + + private IRemoteFile createRemoteFile(String string) + throws FileCreateException, SecurityException { + URLRemoteFile file = null; + IFileID id = null; + String[] parts = string.split("\\|"); + + // Check to see if this string can be parsed + if (parts.length < 4) { + id = FileIDFactory.getDefault().createFileID( + IDFactory.getDefault().getNamespaceByName( + FileTransferNamespace.PROTOCOL), "scp://unknown"); + file = new URLRemoteFile(0, 0, id); + } else { + + // Build the filename back up, since the filename may also contain + // "|" + // characters + StringBuilder builder = new StringBuilder("scp://"); + for (int i = 3; i < parts.length; i++) { + builder.append(parts[i]); + + // Put the | back into the name + if (i > 3 && i < parts.length - 1) { + builder.append("|"); + } + } + + // If it's a directory, then make sure it ends with / + if (parts[0].equals("directory") + && !builder.toString().endsWith("/")) { + builder.append("/"); + } else if (!parts[0].equals("directory") + && builder.toString().endsWith("/")) { + builder.deleteCharAt(builder.length() - 1); + } + + // Create the FileID + id = FileIDFactory.getDefault() + .createFileID( + IDFactory.getDefault().getNamespaceByName( + FileTransferNamespace.PROTOCOL), + builder.toString()); + long size = Long.parseLong(parts[1]); + long modification = Long.parseLong(parts[2]); + file = new URLRemoteFile(modification, size, id); + } + return file; + } + + protected void cleanUp() { + super.cleanUp(); + // FIXME - This code is from ScpOutgoingFileTransfer, but it throws + // exceptions + // if (scpUtil != null) { + // scpUtil.sendZeroToStream(outputStream); + // scpUtil.checkAck(inputStream); + // } + if (channel != null) { + channel.disconnect(); + channel = null; + } + if (scpUtil != null) { + scpUtil.dispose(); + scpUtil = null; + } + try { + if (inputStream != null) + inputStream.close(); + } catch (final IOException e) { + Activator.getDefault().log( + new Status(IStatus.ERROR, Activator.PLUGIN_ID, + IStatus.ERROR, "cleanup", e)); //$NON-NLS-1$ + } + try { + if (outputStream != null) + outputStream.close(); + } catch (final IOException e) { + Activator.getDefault().log( + new Status(IStatus.ERROR, Activator.PLUGIN_ID, + IStatus.ERROR, "cleanup", e)); //$NON-NLS-1$ + } + inputStream = null; + outputStream = null; + } + + protected void setupProxy(Proxy proxy) { + this.proxy = proxy; + this.setupProxies(); + } + + protected void setInputStream(InputStream ins) { + inputStream = ins; + } + + protected void setOutputStream(OutputStream outs) { + outputStream = outs; + } + + public IConnectContext getConnectContext() { + return connectContext; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Proxy getProxy() { + return proxy; + } + + public URL getTargetURL() { + return directoryOrFile; + } + + public Map getOptions() { + return null; + } + +} diff --git a/tests/bundles/org.eclipse.ecf.tests.filetransfer/src/org/eclipse/ecf/tests/filetransfer/ScpFileBrowseTest.java b/tests/bundles/org.eclipse.ecf.tests.filetransfer/src/org/eclipse/ecf/tests/filetransfer/ScpFileBrowseTest.java new file mode 100644 index 000000000..de582fabd --- /dev/null +++ b/tests/bundles/org.eclipse.ecf.tests.filetransfer/src/org/eclipse/ecf/tests/filetransfer/ScpFileBrowseTest.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2014 CohesionForce Inc + * 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: + * CohesionForce Inc - initial API and implementation + *******************************************************************************/ + +package org.eclipse.ecf.tests.filetransfer; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.ecf.core.security.ConnectContextFactory; +import org.eclipse.ecf.core.security.IConnectContext; +import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemBrowseEvent; + +public class ScpFileBrowseTest extends AbstractBrowseTestCase { + + protected File[] roots; + + protected File[] files; + + // Using a countdown latch to wait until we get the proper number + // of browse results + CountDownLatch latch = new CountDownLatch(1); + + String username; + + protected void setUp() throws Exception { + super.setUp(); + IConnectContext cctx; + username = System.getProperty("user.name"); + cctx = ConnectContextFactory.createUsernamePasswordConnectContext( + username, null); + + this.adapter.setConnectContextForAuthentication(cctx); + + roots = File.listRoots(); + final List files = new ArrayList(); + for (int i = 0; i < roots.length; i++) { + final File[] fs = roots[i].listFiles(); + if (fs != null) + for (int j = 0; j < fs.length; j++) { + if (fs[j].exists()) + files.add(fs[j]); + } + } + this.files = (File[]) files.toArray(new File[] {}); + } + + protected void tearDown() throws Exception { + super.tearDown(); + this.roots = null; + this.files = null; + } + + public void testBrowseRoots() throws Exception { + latch = new CountDownLatch(roots.length); + for (int i = 0; i < roots.length; i++) { + if (roots[i].exists()) { + URL url = new URL("scp://"+username+"@localhost:" + + roots[i].getAbsolutePath()); + System.out.println("Browsing: " + url); + testBrowse(url); + } else { + System.out.println("Skipping: " + roots[i].toString()); + latch.countDown(); + } + // Need to sleep to give the connection time to close out + Thread.sleep(100); + } + assertTrue(latch.await(60, TimeUnit.SECONDS)); + } + + @Override + protected void handleFileSystemBrowseEvent(IRemoteFileSystemBrowseEvent event) { + trace("handleFileSystemBrowseEvent(" + event + ")"); + if (event.getException() != null) { + trace(event.getException().toString()); + } + latch.countDown(); + } + + public void testFileBrowse() throws Exception { + latch = new CountDownLatch(files.length); + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory() && files[i].exists()) { + URL url = new URL("scp://"+username+"@localhost:" + + files[i].getAbsolutePath()); + System.out.println("Browsing: " + url); + testBrowse(url); + } else { + System.out.println("Skipping: " + files[i].toString()); + latch.countDown(); + } + // Need to sleep to give the connection time to close out + Thread.sleep(100); + } + assertTrue(latch.await(60, TimeUnit.SECONDS)); + } + +} |