| package org.eclipse.dltk.ssh.internal.core; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.Date; |
| import java.util.Vector; |
| |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.dltk.ssh.core.ISshConnection; |
| import org.eclipse.dltk.ssh.core.ISshFileHandle; |
| import org.eclipse.jsch.core.IJSchService; |
| |
| import com.jcraft.jsch.ChannelSftp; |
| import com.jcraft.jsch.JSchException; |
| import com.jcraft.jsch.Session; |
| import com.jcraft.jsch.SftpATTRS; |
| import com.jcraft.jsch.SftpException; |
| import com.jcraft.jsch.UIKeyboardInteractive; |
| import com.jcraft.jsch.UserInfo; |
| import com.jcraft.jsch.ChannelSftp.LsEntry; |
| |
| /** |
| * TODO: Add correct operation synchronization. |
| * |
| */ |
| public class SshConnection implements ISshConnection { |
| private long disabledTime = 0; |
| |
| private final class LocalUserInfo implements UserInfo, |
| UIKeyboardInteractive { |
| public void showMessage(String arg0) { |
| } |
| |
| public boolean promptYesNo(String arg0) { |
| return false; |
| } |
| |
| public boolean promptPassword(String arg0) { |
| return true; |
| } |
| |
| public boolean promptPassphrase(String arg0) { |
| return false; |
| } |
| |
| public String getPassword() { |
| return password; |
| } |
| |
| public String getPassphrase() { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| public String[] promptKeyboardInteractive(String destination, |
| String name, String instruction, String[] prompt, boolean[] echo) { |
| final String p = password; |
| return p != null ? new String[] { p } : null; |
| } |
| } |
| |
| private static abstract class Operation { |
| boolean finished = false; |
| |
| public abstract void perform() throws JSchException, SftpException; |
| |
| public void setFinished() { |
| finished = true; |
| } |
| |
| public boolean isFinished() { |
| return finished; |
| } |
| |
| public void failed() { |
| } |
| } |
| |
| private class GetStatOperation extends Operation { |
| private IPath path; |
| private SftpATTRS attrs; |
| |
| public GetStatOperation(IPath path) { |
| this.path = path; |
| } |
| |
| @Override |
| public String toString() { |
| return "Get information for file:" + path; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void perform() throws JSchException, SftpException { |
| attrs = getChannel().stat(path.toString()); |
| } |
| |
| public SftpATTRS getAttrs() { |
| return attrs; |
| } |
| } |
| |
| private class ResolveLinkOperation extends Operation { |
| private IPath path; |
| private IPath resolvedPath; |
| |
| public ResolveLinkOperation(IPath path) { |
| this.path = path; |
| } |
| |
| @Override |
| public String toString() { |
| return "Resolve link information for file:" + path; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void perform() throws JSchException, SftpException { |
| SftpATTRS attrs = channel.stat(path.toString()); |
| boolean isRoot = (path.segmentCount() == 0); |
| String linkTarget = null; |
| String canonicalPath; |
| String parentPath = path.removeLastSegments(1).toString(); |
| if (attrs.isLink() && !isRoot) { |
| try { |
| String fullPath = path.toString(); |
| boolean readlinkDone = false; |
| try { |
| linkTarget = channel.readlink(fullPath); |
| readlinkDone = true; |
| } catch (Throwable t) { |
| channel.cd(fullPath); |
| linkTarget = channel.pwd(); |
| canonicalPath = linkTarget; |
| } |
| if (linkTarget != null && !linkTarget.equals(fullPath)) { |
| if (readlinkDone) { |
| String curdir = channel.pwd(); |
| if (!parentPath.equals(curdir)) { |
| channel.cd(parentPath); |
| } |
| } |
| SftpATTRS attrsTarget = channel.stat(linkTarget); |
| if (readlinkDone && attrsTarget.isDir()) { |
| channel.cd(fullPath); |
| canonicalPath = channel.pwd(); |
| } |
| } else { |
| linkTarget = null; |
| } |
| } catch (Exception e) { |
| if (e instanceof SftpException |
| && ((SftpException) e).id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { |
| if (linkTarget == null) { |
| linkTarget = ":dangling link"; //$NON-NLS-1$ |
| } else { |
| linkTarget = ":dangling link:" + linkTarget; //$NON-NLS-1$ |
| } |
| } |
| } |
| resolvedPath = new Path(linkTarget); |
| } |
| } |
| |
| public IPath getResolvedPath() { |
| return resolvedPath; |
| } |
| } |
| |
| private class GetOperation extends Operation { |
| private IPath path; |
| private InputStream stream; |
| |
| public GetOperation(IPath path) { |
| this.path = path; |
| } |
| |
| @Override |
| public void perform() throws JSchException, SftpException { |
| stream = channel.get(path.toString()); |
| performStreamOperation = true; |
| } |
| |
| @Override |
| public String toString() { |
| return "Get input stream for file:" + path; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void failed() { |
| // channel.disconnect(); |
| // channel = null; |
| } |
| |
| public InputStream getStream() { |
| if (stream != null) { |
| InputStream wrapperStream = new BufferedInputStream(stream, |
| 32000) { |
| @Override |
| public void close() throws IOException { |
| super.close(); |
| // channel.disconnect(); |
| // channel = null; |
| doneStreamOperation(); |
| } |
| }; |
| return wrapperStream; |
| } |
| return stream; |
| } |
| |
| } |
| |
| protected synchronized void doneStreamOperation() { |
| performStreamOperation = false; |
| notifyAll(); |
| } |
| |
| private class PutOperation extends Operation { |
| private IPath path; |
| private OutputStream stream; |
| |
| public PutOperation(IPath path) { |
| this.path = path; |
| } |
| |
| @Override |
| public String toString() { |
| return "Get output stream for file:" + path; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void perform() throws JSchException, SftpException { |
| stream = channel.put(path.toString(), ChannelSftp.OVERWRITE); |
| performStreamOperation = true; |
| } |
| |
| public OutputStream getStream() { |
| if (stream != null) { |
| OutputStream wrapperStream = new BufferedOutputStream(stream, |
| 32000) { |
| @Override |
| public void close() throws IOException { |
| super.close(); |
| channel.disconnect(); |
| channel = null; |
| doneStreamOperation(); |
| } |
| }; |
| return wrapperStream; |
| } |
| return stream; |
| } |
| } |
| |
| private class ListFolderOperation extends Operation { |
| private IPath path; |
| private Vector<LsEntry> v; |
| |
| public ListFolderOperation(IPath path) { |
| this.path = path; |
| } |
| |
| @Override |
| public String toString() { |
| return "List folder:" + path + " for files"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public void perform() throws JSchException, SftpException { |
| v = getChannel().ls(path.toString()); |
| } |
| |
| public Vector<LsEntry> getVector() { |
| return v; |
| } |
| } |
| |
| private static final int DEFAULT_RETRY_COUNT = 2; |
| |
| // private static final long TIMEOUT = 3000; // One second timeout |
| |
| private Session session; |
| private String userName; |
| private String password; |
| private int port; |
| private ChannelSftp channel; |
| |
| private String hostName; |
| |
| public SshConnection(String userName, String hostName, int port) { |
| this.userName = userName; |
| this.hostName = hostName; |
| this.port = port; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.dltk.ssh.core.ISshConnection#setPassword(java.lang.String) |
| */ |
| public void setPassword(String password) { |
| this.password = password; |
| } |
| |
| public boolean connect() { |
| return connect(0); |
| } |
| |
| public synchronized boolean connect(int trycount) { |
| try { |
| if (session == null) { |
| IJSchService service = Activator.getDefault().getJSch(); |
| session = service.createSession(hostName, port, userName); |
| session.setTimeout(0); |
| session.setServerAliveInterval(300000); |
| session.setServerAliveCountMax(6); |
| session.setPassword(password); // Set password directly |
| UserInfo ui = new LocalUserInfo(); |
| session.setUserInfo(ui); |
| } |
| |
| if (!session.isConnected()) { |
| session.connect(60 * 1000); // Connect with defautl timeout |
| } |
| |
| if (channel == null) { |
| channel = (ChannelSftp) session.openChannel("sftp"); //$NON-NLS-1$ |
| } |
| if (!channel.isConnected()) { |
| channel.connect(); |
| } |
| } catch (JSchException e) { |
| String eToStr = e.toString(); |
| boolean needLog = true; |
| if (eToStr.indexOf("Auth cancel") >= 0 || eToStr.indexOf("Auth fail") >= 0 || eToStr.indexOf("session is down") >= 0) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| if (session.isConnected()) { |
| session.disconnect(); |
| session = null; |
| } |
| } |
| if (session != null) { |
| session.disconnect(); |
| session = null; |
| } |
| if (needLog) { |
| Activator.error("Failed to create direct connection", e); //$NON-NLS-1$ |
| } |
| } |
| if (session == null || !session.isConnected() || channel == null |
| || !channel.isConnected()) { |
| if (trycount > 0) { |
| if (session == null || !session.isConnected()) { |
| session = null; |
| } |
| channel = null; |
| return connect(trycount - 1); |
| } else { |
| // Lets disable connection for a while. |
| setDisabled(1000 * 10); // 10 seconds |
| // seconds |
| } |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.dltk.ssh.core.ISshConnection#disconnect() |
| */ |
| public void disconnect() { |
| if (channel != null && channel.isConnected()) { |
| channel.disconnect(); |
| channel = null; |
| } |
| if (session != null && session.isConnected()) { |
| session.disconnect(); |
| session = null; |
| } |
| } |
| |
| private boolean performStreamOperation = false; |
| |
| private synchronized void performOperation(final Operation op, int tryCount) { |
| while (performStreamOperation) { |
| try { |
| this.wait(10); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| try { |
| connect(); |
| op.perform(); |
| op.setFinished(); |
| } catch (JSchException ex) { |
| Activator.log(ex); |
| // if (!channel.isConnected()) { |
| if (tryCount > 0) { |
| performOperation(op, tryCount - 1); |
| } |
| // } |
| } catch (SftpException e) { |
| if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) { |
| if (e.id == ChannelSftp.SSH_FX_PERMISSION_DENIED) { |
| Activator.log("Permission denied to perform:" //$NON-NLS-1$ |
| + op.toString()); |
| } else { |
| Activator.log(e); |
| } |
| } |
| |
| } finally { |
| if (!op.isFinished()) { |
| op.failed(); |
| } |
| } |
| |
| } |
| |
| private ChannelSftp getChannel() { |
| return channel; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.dltk.ssh.core.ISshConnection#getHandle(org.eclipse.core.runtime |
| * .IPath) |
| */ |
| public ISshFileHandle getHandle(IPath path) throws Exception { |
| if (isDisabled()) { |
| return null; |
| } |
| // GetStatOperation op = new GetStatOperation(path); |
| // performOperation(op, DEFAULT_RETRY_COUNT); |
| // if (op.isFinished()) { |
| // return new SshFileHandle(this, path, op.getAttrs()); |
| // } |
| return new SshFileHandle(this, path, null); |
| } |
| |
| public boolean isDisabled() { |
| return disabledTime > System.currentTimeMillis(); |
| }; |
| |
| public void setDisabled(int timeout) { |
| disabledTime = System.currentTimeMillis() + timeout; |
| }; |
| |
| SftpATTRS getAttrs(IPath path) { |
| GetStatOperation op = new GetStatOperation(path); |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| if (op.isFinished()) { |
| return op.getAttrs(); |
| } |
| return null; |
| } |
| |
| IPath getResolvedPath(IPath path) { |
| ResolveLinkOperation op = new ResolveLinkOperation(path); |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| if (op.isFinished()) { |
| return op.getResolvedPath(); |
| } |
| return null; |
| } |
| |
| Vector<LsEntry> list(IPath path) { |
| ListFolderOperation op = new ListFolderOperation(path); |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| if (op.isFinished()) { |
| return op.getVector(); |
| } |
| return null; |
| } |
| |
| void setLastModified(final IPath path, final long timestamp) { |
| Operation op = new Operation() { |
| @Override |
| public void perform() throws JSchException, SftpException { |
| Date date = new Date(timestamp); |
| System.out.println(date.toString()); |
| getChannel().setMtime(path.toString(), |
| (int) (timestamp / 1000L)); |
| } |
| }; |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| } |
| |
| void delete(final IPath path, final boolean dir) { |
| Operation op = new Operation() { |
| @Override |
| public void perform() throws JSchException, SftpException { |
| if (!dir) { |
| getChannel().rm(path.toString()); |
| } else { |
| getChannel().rmdir(path.toString()); |
| } |
| } |
| }; |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| } |
| |
| void mkdir(final IPath path) { |
| Operation op = new Operation() { |
| @Override |
| public void perform() throws JSchException, SftpException { |
| getChannel().mkdir(path.toString()); |
| } |
| }; |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| } |
| |
| InputStream get(IPath path) { |
| GetOperation op = new GetOperation(path); |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| if (op.isFinished()) { |
| return op.getStream(); |
| } |
| return null; |
| } |
| |
| OutputStream put(IPath path) { |
| PutOperation op = new PutOperation(path); |
| performOperation(op, DEFAULT_RETRY_COUNT); |
| if (op.isFinished()) { |
| return op.getStream(); |
| } |
| return null; |
| } |
| |
| public boolean isConnected() { |
| if (session != null) { |
| return session.isConnected(); |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result |
| + ((hostName == null) ? 0 : hostName.hashCode()); |
| result = prime * result + port; |
| result = prime * result |
| + ((userName == null) ? 0 : userName.hashCode()); |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| SshConnection other = (SshConnection) obj; |
| if (hostName == null) { |
| if (other.hostName != null) |
| return false; |
| } else if (!hostName.equals(other.hostName)) |
| return false; |
| if (port != other.port) |
| return false; |
| if (userName == null) { |
| if (other.userName != null) |
| return false; |
| } else if (!userName.equals(other.userName)) |
| return false; |
| return true; |
| } |
| |
| } |