diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/.classpath b/rse/plugins/org.eclipse.rse.subsystems.files.scp/.classpath new file mode 100644 index 00000000000..64c5e31b7a2 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/.project b/rse/plugins/org.eclipse.rse.subsystems.files.scp/.project new file mode 100644 index 00000000000..dc11c14fec7 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/.project @@ -0,0 +1,28 @@ + + + org.eclipse.rse.subsystems.files.scp + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/.settings/org.eclipse.jdt.core.prefs b/rse/plugins/org.eclipse.rse.subsystems.files.scp/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..1fbfa75195a --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +#Fri Feb 06 22:40:19 JST 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/META-INF/MANIFEST.MF b/rse/plugins/org.eclipse.rse.subsystems.files.scp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..021e8535740 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.rse.subsystems.files.scp;singleton:=true +Bundle-Version: 0.1.0.qualifier +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Require-Bundle: org.eclipse.rse.subsystems.files.core;bundle-version="[3.0.0,4.0.0)", + org.eclipse.rse.services.ssh;bundle-version="[3.0.0,3.1.0)", + org.eclipse.rse.connectorservice.ssh;bundle-version="[2.0.0,2.2.0)", + org.eclipse.rse.services;bundle-version="[3.0.0,4.0.0)", + org.eclipse.rse.core;bundle-version="[3.0.0,4.0.0)", + org.eclipse.rse.ui;bundle-version="[3.0.0,4.0.0)", + com.jcraft.jsch;bundle-version="[0.1.31,1.0.0)", + org.eclipse.core.runtime;bundle-version="[3.4.0,3.7.0)" +Bundle-Activator: org.eclipse.rse.internal.subsystems.files.scp.Activator +Export-Package: org.eclipse.rse.internal.subsystems.files.scp;x-internal:=true, + org.eclipse.rse.subsystems.files.scp diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/about.html b/rse/plugins/org.eclipse.rse.subsystems.files.scp/about.html new file mode 100644 index 00000000000..d4cc693f9f0 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

June 5, 2007

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/build.properties b/rse/plugins/org.eclipse.rse.subsystems.files.scp/build.properties new file mode 100644 index 00000000000..0a50975014f --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + icons/,\ + plugin.properties,\ + plugin.xml diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/icons/full/obj16/systemfiles_obj.gif b/rse/plugins/org.eclipse.rse.subsystems.files.scp/icons/full/obj16/systemfiles_obj.gif new file mode 100644 index 00000000000..874c9926215 Binary files /dev/null and b/rse/plugins/org.eclipse.rse.subsystems.files.scp/icons/full/obj16/systemfiles_obj.gif differ diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/icons/full/obj16/systemfileslive_obj.gif b/rse/plugins/org.eclipse.rse.subsystems.files.scp/icons/full/obj16/systemfileslive_obj.gif new file mode 100644 index 00000000000..885b8a69e39 Binary files /dev/null and b/rse/plugins/org.eclipse.rse.subsystems.files.scp/icons/full/obj16/systemfileslive_obj.gif differ diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/plugin.properties b/rse/plugins/org.eclipse.rse.subsystems.files.scp/plugin.properties new file mode 100644 index 00000000000..e34f7d92dc9 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/plugin.properties @@ -0,0 +1,16 @@ +################################################################################ +# Copyright (c) 2010 Mentor Graphics 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: +# Nikita Shulga - initial API and implementation +################################################################################ + +pluginName = RSE SCP Files (Incubation) +providerName = Eclipse.org - DSDP + +ScpFileSubsystemName=Scp Files +ScpFileSubsystemDescription=Work with files on remote systems using the Secure Shell (ssh) protocol. diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/plugin.xml b/rse/plugins/org.eclipse.rse.subsystems.files.scp/plugin.xml new file mode 100644 index 00000000000..b42eca23b99 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/plugin.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/Activator.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/Activator.java new file mode 100644 index 00000000000..600762a7099 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/Activator.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Mentor Graphics 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: + * Nikita Shulga (Mentor Graphics) - initial implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.subsystems.files.scp; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +public class Activator extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = Activator.class.getCanonicalName(); + + private static Activator plugin = null; + private static Bundle bundle = null; + + public Activator() { + } + + public static Plugin getDefault() { + return plugin; + } + + public static Bundle getDefaultBundle() { + return bundle; + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + bundle = context.getBundle(); + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + bundle = null; + super.stop(context); + } + + public static void log(String msg) { + log(msg, null); + } + + public static void log(String msg, Exception e) { + log(IStatus.INFO, msg, e); + } + + public static void warn(String msg, Exception e) { + log(IStatus.WARNING, msg, e); + } + + public static void log(int sev, String msg, Exception e) { + Platform.getLog(getDefaultBundle()).log( + new Status(sev, PLUGIN_ID, msg, e)); + } + +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileAdapter.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileAdapter.java new file mode 100644 index 00000000000..7ea96133bdc --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileAdapter.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006, 2010 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (Wind River) - Adapted from FTPFileAdapter. + * Martin Oberhuber (Wind River) - [235363][api][breaking] IHostFileToRemoteFileAdapter methods should return AbstractRemoteFile + * Nikita Shulga (Mentor Graphics) - Adapted from SftpFileAdapter. + *******************************************************************************/ + +package org.eclipse.rse.internal.subsystems.files.scp; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rse.services.files.IHostFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.AbstractRemoteFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystem; +import org.eclipse.rse.subsystems.files.core.subsystems.IHostFileToRemoteFileAdapter; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileContext; + +public class ScpFileAdapter implements IHostFileToRemoteFileAdapter { + + public AbstractRemoteFile convertToRemoteFile(FileServiceSubSystem ss, + IRemoteFileContext context, IRemoteFile parent, IHostFile node) { + + ScpRemoteFile file = new ScpRemoteFile(ss, context, parent, node); + ss.cacheRemoteFile(file); + return file; + } + + public AbstractRemoteFile[] convertToRemoteFiles(FileServiceSubSystem ss, + IRemoteFileContext context, IRemoteFile parent, IHostFile[] nodes) { + + if (nodes == null) + return new AbstractRemoteFile[0]; + + List results = new ArrayList(); + for (IHostFile node : nodes) { + ScpRemoteFile file = new ScpRemoteFile(ss, context, parent, node); + ss.cacheRemoteFile(file); + results.add(file); + } + + return (ScpRemoteFile[]) results.toArray(new ScpRemoteFile[results + .size()]); + } + +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileAttr.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileAttr.java new file mode 100644 index 00000000000..ab9bf1ac99c --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileAttr.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Mentor Graphics 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: + * Nikita Shulga - initial API and implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.subsystems.files.scp; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Stack; +import java.util.regex.Pattern; + +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; +import org.eclipse.rse.services.files.HostFilePermissions; +import org.eclipse.rse.services.files.IHostFilePermissions; + +import com.jcraft.jsch.Session; + +public class ScpFileAttr { + private String lsString; + private String attrString = null; + private int linkNo = 0; + private long mTime = 0; + private String user = null; + private String group = null; + private long size = -1; + private String name = null; + private String linkName = null; + + public IHostFilePermissions getFilePermissions() { + return new HostFilePermissions(getAttrs(), getUser(), getGroup()); + } + + public long getMTime() { + if (!initialized) + doSplit(); + return mTime; + } + + public long getSize() { + if (!initialized) + doSplit(); + return size; + } + + public String getUser() { + if (!initialized) + doSplit(); + return user; + } + + public String getGroup() { + if (!initialized) + doSplit(); + return group; + } + + public int getLinkNo() { + if (!initialized) + doSplit(); + return linkNo; + } + + public String getName() { + if (!initialized) + doSplit(); + return name; + } + + public String getLinkName() { + if (!initialized) + doSplit(); + return linkName; + } + + public String getAttrs() { + if (!initialized) + doSplit(); + + if (attrString == null || attrString.length() < 9) + return "---------"; //$NON-NLS-1$ + + return attrString; + } + + private char getFileType() { + String attrs = getAttrs(); + if (attrs == null || attrs.length() == 0) + return 0; + return attrs.charAt(0); + } + + public boolean isBlockDevice() { + return getFileType() == 'b'; + } + + public boolean isCharDevice() { + return getFileType() == 'c'; + } + + public boolean isLink() { + return getFileType() == 'l'; + } + + public boolean isDirectory() { + return getFileType() == 'd'; + } + + ScpFileAttr(String str) { + lsString = str; + if (lsString.endsWith("\n")) //$NON-NLS-1$ + lsString = lsString.substring(0, lsString.length() - 1); + } + + private boolean initialized = false; + + private void doSplit() { + if (initialized) + return; + initialized = true; + try { + SplitAux(); + } catch (Exception e) { + Activator.warn( + "ScpFileAttr:Exception occured while splitting string " + + lsString, e); + } + } + + /* + * ls command uses two short date formats: - for files modified more than 6 + * month ago - for files modified less than 6 month ago + */ + final static DateFormat lessThanSixMonthOldFormat = new SimpleDateFormat( + "MMM dd HH:mm"); //$NON-NLS-1$ + final static DateFormat moreThanSixMonthOldFormat = new SimpleDateFormat( + "MMM dd yyyy"); //$NON-NLS-1$ + + /** + * Parses date time string returned by ls command + * + * @param date + * - file modification date as string + * @return time in seconds since start of epoch until mTime + */ + static long parseDateTime(String date) { + // TODO: Add support for ls -le date format + try { + Date d = lessThanSixMonthOldFormat.parse(date); + Calendar c = Calendar.getInstance(); + c.setTime(d); + c.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR)); + return c.getTimeInMillis() / 1000; + } catch (Exception e) { + } + try { + Date d = moreThanSixMonthOldFormat.parse(date); + return d.getTime() / 1000; + } catch (Exception e) { + } + return 0; + } + + private static Pattern lsPattern = Pattern.compile("\\s+"); //$NON-NLS-1$ + + public void SplitAux() throws Exception { + Stack fields = new Stack(); + for (String s : lsPattern.split(lsString)) + fields.insertElementAt(s, 0); + + /* store file attributes */ + if (fields.empty()) + return; + attrString = fields.pop(); + + /* store link number */ + if (fields.empty()) + return; + linkNo = Integer.parseInt(fields.pop()); + + /* store uid and gid */ + if (fields.empty()) + return; + user = fields.pop(); + if (fields.empty()) + return; + group = fields.pop(); + + /* store file size */ + if (fields.empty()) + return; + if (!isCharDevice() && !isBlockDevice()) + size = Long.parseLong(fields.pop()); + else { + /* Size is undefined for character and block devices */ + size = 0; + /* And we don't know what to do with devs major/minor */ + fields.pop(); + if (fields.empty()) + return; + fields.pop(); + } + + /* Date always takes three fields */ + if (fields.empty()) + return; + String dateField = fields.pop(); + if (fields.empty()) + return; + dateField = dateField + " " + fields.pop(); //$NON-NLS-1$ + if (fields.empty()) + return; + dateField = dateField + " " + fields.pop(); //$NON-NLS-1$ + + mTime = parseDateTime(dateField); + /* The rest of the entry is name ( and may be symlink ) */ + String[] namesplit = Pattern.compile( + dateField.replace(" ", "\\s+") + "\\s").split(lsString); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + if (namesplit.length != 2) + return; + name = namesplit[1]; + if (isLink()) { + namesplit = name.split(" -> "); //$NON-NLS-1$ + if (namesplit.length != 2) + return; + name = namesplit[0]; + linkName = namesplit[1]; + } + } + + public static ScpFileAttr getAttr(Session sess, String path) + throws SystemMessageException { + String attr = ScpFileUtils.execCommandSafe(sess, "ls -land " //$NON-NLS-1$ + + ScpFileUtils.escapePath(path)); + if (attr == null || attr.length() < 9) + return null; + return new ScpFileAttr(attr); + } + +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileService.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileService.java new file mode 100644 index 00000000000..9d6972d0c23 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileService.java @@ -0,0 +1,522 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Mentor Graphics 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: + * Nikita Shulga - initial API and implementation + *******************************************************************************/ +package org.eclipse.rse.internal.subsystems.files.scp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.rse.internal.services.ssh.ISshSessionProvider; +import org.eclipse.rse.internal.services.ssh.files.SftpHostFile; +import org.eclipse.rse.services.clientserver.FileTypeMatcher; +import org.eclipse.rse.services.clientserver.IMatcher; +import org.eclipse.rse.services.clientserver.NamePatternMatcher; +import org.eclipse.rse.services.clientserver.messages.SimpleSystemMessage; +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; +import org.eclipse.rse.services.files.AbstractFileService; +import org.eclipse.rse.services.files.HostFilePermissions; +import org.eclipse.rse.services.files.IFilePermissionsService; +import org.eclipse.rse.services.files.IHostFile; +import org.eclipse.rse.services.files.IHostFilePermissions; +import org.eclipse.rse.services.files.IHostFilePermissionsContainer; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.Session; + +@SuppressWarnings("restriction") +public class ScpFileService extends AbstractFileService implements + IFilePermissionsService { + + private final ISshSessionProvider fSessionProvider; + private String fUserHome = null; + + public ISshSessionProvider getSessionProvider() { + return fSessionProvider; + } + + public Session getSession() { + return getSessionProvider().getSession(); + } + + public ScpFileService(ISshSessionProvider provider) { + fSessionProvider = provider; + } + + protected static void throwSystemException(String msg) + throws SystemMessageException { + throw new SystemMessageException(new SimpleSystemMessage( + Activator.PLUGIN_ID, IStatus.ERROR, msg)); + } + + protected static void throwSystemException(Exception e) + throws SystemMessageException { + Activator.warn("ScpFileServie.throwSystemExcpeption", e); + + if (e instanceof SystemMessageException) + throw (SystemMessageException) e; + throw new SystemMessageException(new SimpleSystemMessage( + Activator.PLUGIN_ID, IStatus.ERROR, e.getMessage(), e)); + } + + @Override + protected IHostFile[] internalFetch(String parentPath, String fileFilter, + int fileType, IProgressMonitor monitor) + throws SystemMessageException { + + if (fileFilter == null) + fileFilter = "*"; + IMatcher filematcher = null; + if (fileFilter.endsWith(",")) { //$NON-NLS-1$ + String[] types = fileFilter.split(","); //$NON-NLS-1$ + filematcher = new FileTypeMatcher(types, true); + } else { + filematcher = new NamePatternMatcher(fileFilter, true, true); + } + + List results = new ArrayList(); + Session sess = getSession(); + String cmd = "ls -lAn " + ScpFileUtils.escapePath(parentPath); //$NON-NLS-1$ + + String rc = ScpFileUtils.execCommandSafe(sess, cmd); + + for (String lsString : rc.split(ScpFileUtils.EOL_STRING)) { + if (lsString.length() == 0 || lsString.startsWith("total")) //$NON-NLS-1$ + continue; + ScpFileAttr attr = new ScpFileAttr(lsString); + if (attr == null || attr.getName() == null) { + Activator.warn("internalFetch(parentPath='" + parentPath + + "'): Can't get name of " + lsString, null); + continue; + } + if (!filematcher.matches(attr.getName())) + continue; + IHostFile f = makeHostFile(parentPath, null, attr); + if (isRightType(fileType, f)) + results.add(f); + } + return (IHostFile[]) results.toArray(new IHostFile[results.size()]); + } + + public int getCapabilities(IHostFile host) { + return IFilePermissionsService.FS_CAN_GET_ALL + | IFilePermissionsService.FS_CAN_SET_ALL; + } + + public IHostFilePermissions getFilePermissions(IHostFile file, + IProgressMonitor monitor) throws SystemMessageException { + if (file instanceof IHostFilePermissionsContainer) { + return ((IHostFilePermissionsContainer) file).getPermissions(); + } + return null; + } + + public void setFilePermissions(IHostFile file, + IHostFilePermissions permissions, IProgressMonitor monitor) + throws SystemMessageException { + Session session = getSession(); + String path = ScpFileUtils.escapePath(file.getAbsolutePath()); + int permBits = permissions.getPermissionBits(); + String ownStr = permissions.getUserOwner() + ':' + + permissions.getGroupOwner(); + String cmd = "chmod " + Integer.toOctalString(permBits) + " " + path; //$NON-NLS-1$ //$NON-NLS-2$ + ScpFileUtils.execCommandSafe(session, cmd); + ScpFileUtils.execCommandSafe(session, "chown " + ownStr + " " + path); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void copy(String arg0, String arg1, String arg2, String arg3, + IProgressMonitor arg4) throws SystemMessageException { + // TODO Auto-generated method stub + + } + + public void copyBatch(String[] arg0, String[] arg1, String arg2, + IProgressMonitor arg3) throws SystemMessageException { + // TODO Auto-generated method stub + + } + + public IHostFile createFile(String remotePath, String fileName, + IProgressMonitor arg2) throws SystemMessageException { + Session session = getSession(); + String fullPath = ScpFileUtils.concat(remotePath, fileName); + + ScpFileUtils.execCommandSafe(session, + "touch " + ScpFileUtils.escapePath(fullPath)); //$NON-NLS-1$ + ScpFileAttr attr = ScpFileAttr.getAttr(session, fullPath); + + return makeHostFile(remotePath, fileName, attr); + } + + public IHostFile createFolder(String remotePath, String fileName, + IProgressMonitor arg2) throws SystemMessageException { + Session session = getSession(); + String fullPath = ScpFileUtils.concat(remotePath, fileName); + + ScpFileUtils.execCommandSafe(session, + "mkdir " + ScpFileUtils.escapePath(fullPath)); //$NON-NLS-1$ + ScpFileAttr attr = ScpFileAttr.getAttr(session, fullPath); + + return makeHostFile(remotePath, fileName, attr); + } + + public void delete(String remotePath, String fileName, IProgressMonitor arg2) + throws SystemMessageException { + Session session = getSession(); + String fullPathEsc = ScpFileUtils.concatEscape(remotePath, fileName); + + ScpFileUtils.execCommandSafe(session, "rm -rf " + fullPathEsc); //$NON-NLS-1$ + + } + + private static String lastErrorMessage = null; + + private static void setErrorMessage(String s) { + lastErrorMessage = s; + } + + public static String getLastError() { + return lastErrorMessage; + } + + private static int readAck(InputStream is) throws IOException { + int rc = is.read(); + if (rc <= 0) + return rc; + + /* In case of non fatal error had to read an error string */ + StringBuffer sb = new StringBuffer(); + int ch; + do { + ch = is.read(); + sb.append((char) ch); + } while (ch != ScpFileUtils.EOL_CHAR); + setErrorMessage(sb.toString()); + return rc; + } + + public void download(String remoteParent, String fileName, File localFile, + boolean isBinary, String hostEncoding, IProgressMonitor monitor) + throws SystemMessageException { + + try { + if (!localFile.exists()) { + File localParentFile = localFile.getParentFile(); + if (!localParentFile.exists()) { + localParentFile.mkdirs(); + } + } + + internalDownload(remoteParent, fileName, localFile, monitor); + + } catch (Exception e) { + throwSystemException(e); + } + + } + + public IHostFile getFile(String remotePath, String fileName, + IProgressMonitor monitor) throws SystemMessageException { + Session session = getSession(); + String fullPath = ScpFileUtils.concat(remotePath, fileName); + ScpFileAttr attr = ScpFileAttr.getAttr(session, fullPath); + + return makeHostFile(remotePath, fileName, attr); + } + + public IHostFile[] getRoots(IProgressMonitor monitor) + throws SystemMessageException { + IHostFile root = null; + try { + root = getFile(null, ScpFileUtils.TARGET_SEPARATOR, monitor); + } catch (SystemMessageException e) { + Activator.warn("Failed to get root file", e); + } + if (root == null) + root = new SftpHostFile(null, ScpFileUtils.TARGET_SEPARATOR, true, + true, false, 0, 0); + + return new IHostFile[] { root }; + + } + + public IHostFile getUserHome() { + if (fUserHome == null) { + try { + Session sess = getSession(); + fUserHome = ScpFileUtils.execCommand(sess, "pwd").split( //$NON-NLS-1$ + ScpFileUtils.EOL_STRING)[0]; + } catch (Exception e) { + Activator.warn("Failed to execute pwd", e); + return null; + } + } + if (fUserHome == null) + return null; + int lastSlash = fUserHome + .lastIndexOf(ScpFileUtils.TARGET_SEPARATOR_CHAR); + String name = fUserHome.substring(lastSlash + 1); + String parent = fUserHome.substring(0, lastSlash); + IHostFile rc = null; + try { + rc = getFile(parent, name, null); + } catch (SystemMessageException e) { + Activator.warn("Failed to get user home file ", e); + } + if (rc == null) + rc = new SftpHostFile(null, fUserHome, true, true, false, 0, 0); + return rc; + } + + public boolean isCaseSensitive() { + return true; + } + + public void move(String oldPath, String oldName, String newPath, + String newName, IProgressMonitor arg4) + throws SystemMessageException { + Session session = getSession(); + String oldFullPathEsc = ScpFileUtils.concatEscape(newPath, oldName); + String newFullPathEsc = ScpFileUtils.concatEscape(newPath, newName); + + ScpFileUtils.execCommandSafe(session, "mv " + oldFullPathEsc + " " //$NON-NLS-1$ //$NON-NLS-2$ + + newFullPathEsc); + } + + public void rename(String remotePath, String oldName, String newName, + IProgressMonitor monitor) throws SystemMessageException { + Session session = getSession(); + String oldFullPathEsc = ScpFileUtils.concatEscape(remotePath, oldName); + String newFullPathEsc = ScpFileUtils.concatEscape(remotePath, newName); + + ScpFileUtils.execCommandSafe(session, "mv " + oldFullPathEsc + " " //$NON-NLS-1$ //$NON-NLS-2$ + + newFullPathEsc); + + } + + public void rename(String remotePath, String oldName, String newName, + IHostFile hostFile, IProgressMonitor monitor) + throws SystemMessageException { + // hostFile remains unused + rename(remotePath, oldName, newName, monitor); + } + + public void setLastModified(String arg0, String arg1, long arg2, + IProgressMonitor arg3) throws SystemMessageException { + throwSystemException("setLastModified() not supported"); + } + + public void setReadOnly(String remotePath, String fileName, + boolean disableWrite, IProgressMonitor monitor) + throws SystemMessageException { + Session session = getSession(); + String fullPath = ScpFileUtils.concat(remotePath, fileName); + ScpFileAttr attr = ScpFileAttr.getAttr(session, fullPath); + if (attr == null) + throwSystemException("Can't get attribute of file " + fullPath); + int perm = new HostFilePermissions(attr.getAttrs(), "", "") //$NON-NLS-1$ //$NON-NLS-2$ + .getPermissionBits(); + if (disableWrite) + perm &= ~0222; + else + perm |= 0200; + ScpFileUtils.execCommandSafe( + session, + "chmod " + Integer.toOctalString(perm) + " " //$NON-NLS-1$//$NON-NLS-2$ + + ScpFileUtils.escapePath(fullPath)); + } + + public void upload(InputStream stream, String remotePath, + String remoteFile, boolean isBinary, String hostEncoding, + IProgressMonitor monitor) throws SystemMessageException { + + } + + private void internalDownload(String remoteParent, String fileName, + File localFile, IProgressMonitor monitor) throws Exception { + String remotePath = ScpFileUtils.concat(remoteParent, fileName); + String cmd = "scp -f " + ScpFileUtils.escapePath(remotePath); //$NON-NLS-1$ + + ChannelExec ch = ScpFileUtils.openExecChannel(getSession(), cmd); + InputStream is = ch.getInputStream(); + OutputStream os = ch.getOutputStream(); + ch.connect(); + + byte buf[] = new byte[1024]; + // send '0' + buf[0] = 0; + os.write(buf, 0, 1); + os.flush(); + + int c = is.read(); + if (c == 1) { + String errmsg = ScpFileUtils.readString(is); + throw new Exception("Error while downloading " + remotePath + " :" + + errmsg); + } + if (c != 'C') + throw new Exception("Error while downloading " + remotePath + + ": Can't download file of type" + c); + + // read '0644' + is.read(buf, 0, 5); + + // get file size + long filesize = 0; + while (true) { + if (is.read(buf, 0, 1) < 0) + break; + if (buf[0] == ' ') + break; + filesize = filesize * 10 + (buf[0] - '0'); + } + String fname = ScpFileUtils.readString(is); + + Activator.log("filesize=" + filesize + " fname=" + fname); + + // Confirm that file description is read by sending '0' + buf[0] = 0; + os.write(buf, 0, 1); + os.flush(); + + FileOutputStream fos = new FileOutputStream(localFile); + monitor.beginTask("Downloading file", (int) filesize); + long bytesDownloaded = 0; + while (true) { + int len = buf.length; + if (filesize - bytesDownloaded < len) + len = (int) (filesize - bytesDownloaded); + int rc = is.read(buf, 0, len); + if (rc < 0) + break; + monitor.worked(rc); + fos.write(buf, 0, rc); + bytesDownloaded += rc; + if (bytesDownloaded >= filesize) + break; + } + fos.close(); + fos = null; + monitor.done(); + + c = is.read(); + if (c == 0) { + // Confirm that file was received by sending '0' + buf[0] = 0; + os.write(buf, 0, 1); + os.flush(); + } + + os.close(); + is.close(); + + ch.disconnect(); + } + + private void internalUpload(File localFile, String remotePath, + String remoteFile, IProgressMonitor monitor) throws Exception { + + monitor.beginTask("Uploading file", (int) localFile.length() + 10); + + Session session = getSession(); + String cmd = "scp -p -t " + ScpFileUtils.escapePath(remotePath); //$NON-NLS-1$ + ChannelExec ch = ScpFileUtils.openExecChannel(session, cmd); + InputStream is = ch.getInputStream(); + OutputStream os = ch.getOutputStream(); + ch.connect(); + monitor.internalWorked(5); + + String fileHeader = "C0644" + " " + localFile.length() + " " //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ + + remoteFile + "\n"; //$NON-NLS-1$ + if (readAck(is) != 0) + throw new Exception("upload: wrong Ack! Last error:" + + getLastError()); + + os.write(fileHeader.getBytes()); + os.flush(); + + monitor.internalWorked(4); + + FileInputStream fis = new FileInputStream(localFile); + + byte[] buf = new byte[1024]; + while (true) { + int len = fis.read(buf, 0, buf.length); + if (len <= 0) + break; + os.write(buf, 0, len); + monitor.internalWorked(len); + } + fis.close(); + + buf[0] = 0; + os.write(buf, 0, 1); + os.flush(); + if (readAck(is) != 0) + throw new Exception("Error happened while uploading " + + localFile.getAbsolutePath() + ":" + getLastError()); + monitor.internalWorked(1); + + os.close(); + is.close(); + monitor.done(); + ch.disconnect(); + } + + public void upload(File localFile, String remotePath, String remoteFile, + boolean isBinary, String srcEncoding, String hostEncoding, + IProgressMonitor monitor) throws SystemMessageException { + + try { + internalUpload(localFile, remotePath, remoteFile, monitor); + } catch (Exception e) { + throwSystemException(e); + } + + } + + private IHostFile makeHostFile(String remotePath, String fileName, + ScpFileAttr attr) { + boolean isRoot = (remotePath == null || remotePath.length() == 0); + if (attr == null) + return new SftpHostFile(isRoot ? null : remotePath, fileName, + false, isRoot, false, 0, 0); + + boolean isLink = attr.isLink(); + boolean isDir = attr.isDirectory(); + if (fileName == null) + fileName = attr.getName(); + SftpHostFile node = new SftpHostFile(isRoot ? null : remotePath, + fileName, isDir, isRoot, isLink, 1000L * attr.getMTime(), + attr.getSize()); + if (isLink && attr.getLinkName() != null) + node.setLinkTarget(attr.getLinkName()); + node.setPermissions(attr.getFilePermissions()); + return node; + } + + @Override + public String getDescription() { + return "SSH/SCP File Service can be used to connect to embedded sshd implementations, which often lacks sftp service"; + } + + @Override + public String getName() { + return "SCP File Service"; + } + +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileUtils.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileUtils.java new file mode 100644 index 00000000000..816dacaa970 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpFileUtils.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Mentor Graphics 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: + * Nikita Shulga - initial API and implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.subsystems.files.scp; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.Session; + +public class ScpFileUtils { + public static final String EOL_STRING = "\n"; //$NON-NLS-1$ + public static final String TARGET_SEPARATOR = "/"; //$NON-NLS-1$ + public static final String EXEC_CHANNEL = "exec"; //$NON-NLS-1$ + public static final String QUOTATION_MARK = "\""; //$NON-NLS-1$ + + public static char EOL_CHAR = EOL_STRING.charAt(0); + public static char TARGET_SEPARATOR_CHAR = TARGET_SEPARATOR.charAt(0); + + /** + * Concatenate a parent directory with a file name to form a new proper path + * name. + * + * This method was cloned from + * {@link org.eclipse.rse.internal.services.ssh.files.SftpFileService#concat} + */ + protected static String concat(String parentDir, String fileName) { + // See also {@link SftpHostFile#getAbsolutePath()} + if (parentDir == null || parentDir.length() == 0) { + // Looking at a Root + return fileName; + } + + StringBuffer path = new StringBuffer(parentDir); + if (!parentDir.endsWith(TARGET_SEPARATOR)) + path.append(TARGET_SEPARATOR_CHAR); + + path.append(fileName); + return path.toString(); + } + + public static ChannelExec openExecChannel(Session sess) throws Exception { + return (ChannelExec) sess.openChannel(EXEC_CHANNEL); + } + + public static ChannelExec openExecChannel(Session sess, String cmd) + throws Exception { + ChannelExec ch = openExecChannel(sess); + if (ch != null) + ch.setCommand(cmd); + return ch; + } + + public static String execCommand(Session sess, String command) + throws Exception { + ChannelExec ch = openExecChannel(sess, command); + ch.setInputStream(null); + ch.setErrStream(System.err, true); + InputStream is = ch.getInputStream(); + ch.connect(); + + String str = readStream(ch); + is.close(); + ch.disconnect(); + return str; + } + + public static String execCommandSafe(Session session, String command) + throws SystemMessageException { + String rc = null; + try { + rc = execCommand(session, command); + } catch (Exception e) { + ScpFileService.throwSystemException(e); + } + return rc; + } + + public static String escapePath(String path) { + if (path.indexOf(' ') < 0) + return path; + return QUOTATION_MARK + path + QUOTATION_MARK; + } + + /** + * Concatenates parent directory with file name and escape the result if + * necessary + * + * @param parentDir + * parent directory + * @param fileName + * file name + * @return escaped concatenated path to the file + */ + protected static String concatEscape(String parentDir, String fileName) { + return escapePath(concat(parentDir, fileName)); + } + + public static String readString(InputStream is) throws IOException { + StringBuffer buf = new StringBuffer(); + int rc = 0; + while (true) { + rc = is.read(); + if (rc == EOL_CHAR) + break; + buf.append((char) rc); + } + return buf.toString(); + } + + public static String readStream(ChannelExec ch) throws IOException { + InputStream is = ch.getInputStream(); + byte[] buf = new byte[1024]; + String rc = ""; //$NON-NLS-1$ + while (!ch.isClosed() || is.available() > 0) { + int cnt = is.read(buf, 0, buf.length); + if (cnt < 0) + break; + rc += new String(buf, 0, cnt); + } + return rc; + } + +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpRemoteFile.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpRemoteFile.java new file mode 100644 index 00000000000..666f864d6fa --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/internal/subsystems/files/scp/ScpRemoteFile.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2005, 2010 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (Wind River) - Adapted from FTPRemoteFile. + * Martin Oberhuber (Wind River) - [216343] immediate link targets and canonical paths for Sftp + * Nikita Shulga (Mentor Graphics) - Adapted from SftpRemoteFile. + *******************************************************************************/ + +package org.eclipse.rse.internal.subsystems.files.scp; + +import org.eclipse.rse.internal.services.ssh.files.SftpHostFile; +import org.eclipse.rse.services.files.IHostFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.AbstractRemoteFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystem; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileContext; + +@SuppressWarnings("restriction") +public class ScpRemoteFile extends AbstractRemoteFile { + + public ScpRemoteFile(FileServiceSubSystem ss, IRemoteFileContext context, + IRemoteFile parent, IHostFile hostFile) { + super(ss, context, parent, hostFile); + } + + public SftpHostFile getSftpHostFile() { + return (SftpHostFile) getHostFile(); + } + + public String getCanonicalPath() { + String canonicalPath = getSftpHostFile().getCanonicalPath(); + if (canonicalPath.equals(getAbsolutePath()) && _parentFile != null) { + String parentCanonicalPath = _parentFile.getCanonicalPath(); + StringBuffer path = new StringBuffer(parentCanonicalPath); + if (!parentCanonicalPath.endsWith(ScpFileUtils.TARGET_SEPARATOR)) + path.append(ScpFileUtils.TARGET_SEPARATOR); + + path.append(getName()); + canonicalPath = path.toString(); + } + return canonicalPath; + } + + public String getClassification() { + return getSftpHostFile().getClassification(); + } +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/subsystems/files/scp/ScpFileSubSystemConfiguration.java b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/subsystems/files/scp/ScpFileSubSystemConfiguration.java new file mode 100644 index 00000000000..0e0aae2cf8e --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.files.scp/src/org/eclipse/rse/subsystems/files/scp/ScpFileSubSystemConfiguration.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2006, 2010 Wind River Systems, Inc. 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: + * Martin Oberhuber (Wind River) - initial API and implementation + * Nikita Shulga (Mentor Graphics) - adapted from SftpFileSubSystemConfiguration. + *******************************************************************************/ + +package org.eclipse.rse.subsystems.files.scp; + +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.IConnectorService; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.internal.connectorservice.ssh.SshConnectorService; +import org.eclipse.rse.internal.connectorservice.ssh.SshConnectorServiceManager; +import org.eclipse.rse.internal.services.ssh.ISshService; +import org.eclipse.rse.internal.subsystems.files.scp.ScpFileAdapter; +import org.eclipse.rse.internal.subsystems.files.scp.ScpFileService; + +import org.eclipse.rse.services.clientserver.SystemSearchString; +import org.eclipse.rse.services.files.IFileService; +import org.eclipse.rse.services.search.IHostSearchResultConfiguration; +import org.eclipse.rse.services.search.IHostSearchResultSet; +import org.eclipse.rse.services.search.ISearchService; +import org.eclipse.rse.subsystems.files.core.ILanguageUtilityFactory; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystem; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystemConfiguration; +import org.eclipse.rse.subsystems.files.core.subsystems.IHostFileToRemoteFileAdapter; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileSubSystem; + +@SuppressWarnings("restriction") +public class ScpFileSubSystemConfiguration extends + FileServiceSubSystemConfiguration { + + protected IHostFileToRemoteFileAdapter _hostFileAdapter; + + public ScpFileSubSystemConfiguration() { + super(); + setIsUnixStyle(true); + } + + @Override + public ISubSystem createSubSystemInternal(IHost host) { + IConnectorService connectorService = getConnectorService(host); + IFileService fileService = getFileService(host); + ISubSystem subsys = new FileServiceSubSystem(host, connectorService, + fileService, getHostFileAdapter(), getSearchService(host)); + return subsys; + } + + public IFileService createFileService(IHost host) { + SshConnectorService connectorService = (SshConnectorService) getConnectorService(host); + return new ScpFileService(connectorService); + } + + public IHostFileToRemoteFileAdapter getHostFileAdapter() { + if (_hostFileAdapter == null) { + _hostFileAdapter = new ScpFileAdapter(); + } + return _hostFileAdapter; + } + + public ILanguageUtilityFactory getLanguageUtilityFactory( + IRemoteFileSubSystem arg0) { + return null; + } + + public IConnectorService getConnectorService(IHost host) { + return SshConnectorServiceManager.getInstance().getConnectorService( + host, getServiceImplType()); + } + + public Class getServiceImplType() { + return ISshService.class; + } + + public boolean supportsArchiveManagement() { + return false; + } + + public boolean supportsFileTypes() { + return false; + } + + public boolean supportsSearch() { + return false; + } + + public ISearchService createSearchService(IHost arg0) { + // no search service supported for ssh/scp at moment + return null; + } + + public IHostSearchResultConfiguration createSearchConfiguration(IHost arg0, + IHostSearchResultSet arg1, Object arg2, SystemSearchString arg3) { + return null; + } + +}