support sftp versions < 3

This commit is contained in:
Shikhar Bhushan
2010-08-06 00:02:21 +01:00
parent 3cd446b462
commit 3beee8350d
10 changed files with 148 additions and 108 deletions

View File

@@ -33,6 +33,7 @@ import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.Forward
import net.schmizz.sshj.connection.channel.forwarded.X11Forwarder;
import net.schmizz.sshj.connection.channel.forwarded.X11Forwarder.X11Channel;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.sftp.SFTPEngine;
import net.schmizz.sshj.sftp.StatefulSFTPClient;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
@@ -592,8 +593,21 @@ public class SSHClient
*/
public SFTPClient newSFTPClient()
throws IOException {
return newSFTPClient(SFTPEngine.MAX_SUPPORTED_VERSION);
}
/**
* @param version the protocol version to use
*
* @return Instantiated {@link SFTPClient} implementation.
*
* @throws IOException if there is an error starting the {@code sftp} subsystem
* @see StatefulSFTPClient
*/
public SFTPClient newSFTPClient(int version)
throws IOException {
assert isConnected() && isAuthenticated();
return new SFTPClient(this);
return new SFTPClient(new SFTPEngine(this, version).init());
}
/**

View File

@@ -34,9 +34,11 @@ public class PacketReader
private final Map<Long, Future<Response, SFTPException>> futures = new ConcurrentHashMap<Long, Future<Response, SFTPException>>();
private final SFTPPacket<Response> packet = new SFTPPacket<Response>();
private final byte[] lenBuf = new byte[4];
private final SFTPEngine engine;
public PacketReader(InputStream in) {
this.in = in;
public PacketReader(SFTPEngine engine) {
this.engine = engine;
this.in = engine.getSubsystem().getInputStream();
setName("sftp reader");
}
@@ -78,7 +80,7 @@ public class PacketReader
@Override
public void run() {
try {
while (true) {
while (!isInterrupted()) {
readPacket();
handle();
}
@@ -90,7 +92,7 @@ public class PacketReader
public void handle()
throws SFTPException {
Response resp = new Response(packet);
Response resp = new Response(packet, engine.getOperativeProtocolVersion());
Future<Response, SFTPException> future = futures.remove(resp.getRequestID());
log.debug("Received {} packet", resp.getType());
if (future == null)

View File

@@ -19,11 +19,11 @@ import java.io.IOException;
public class PathHelper {
private final SFTPEngine sftp;
private final SFTPEngine engine;
private String dotDir;
public PathHelper(SFTPEngine sftp) {
this.sftp = sftp;
public PathHelper(SFTPEngine engine) {
this.engine = engine;
}
public PathComponents getComponents(String path)
@@ -56,7 +56,7 @@ public class PathHelper {
private String canon(String path)
throws IOException {
return sftp.canonicalize(path);
return engine.canonicalize(path);
}
}

View File

@@ -21,7 +21,7 @@ import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
abstract class RemoteResource
public abstract class RemoteResource
implements Closeable {
/** Logger */

View File

@@ -47,15 +47,21 @@ public class Response
}
private final int protocolVersion;
private final PacketType type;
private final long reqID;
public Response(Buffer<Response> pk) {
public Response(Buffer<Response> pk, int protocolVersion) {
super(pk);
this.protocolVersion = protocolVersion;
this.type = readType();
this.reqID = readLong();
}
public int getProtocolVersion() {
return protocolVersion;
}
public long getRequestID() {
return reqID;
}
@@ -72,7 +78,7 @@ public class Response
throws SFTPException {
if (getType() != pt)
if (getType() == PacketType.STATUS)
throw new SFTPException(readStatusCode(), readString());
error(readStatusCode());
else
throw new SFTPException("Unexpected packet " + getType());
return this;
@@ -85,10 +91,15 @@ public class Response
public Response ensureStatusIs(StatusCode acceptable)
throws SFTPException {
StatusCode sc = readStatusCode();
final StatusCode sc = readStatusCode();
if (sc != acceptable)
throw new SFTPException(sc, readString());
error(sc);
return this;
}
private String error(StatusCode sc)
throws SFTPException {
throw new SFTPException(sc, protocolVersion < 3 ? sc.toString() : readString());
}
}

View File

@@ -15,7 +15,6 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.FilePermission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,19 +33,18 @@ public class SFTPClient
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
private final SFTPEngine sftp;
private final SFTPEngine engine;
private final SFTPFileTransfer xfer;
private final PathHelper pathHelper;
public SFTPClient(SessionFactory ssh)
throws IOException {
this.sftp = new SFTPEngine(ssh).init();
this.pathHelper = new PathHelper(sftp);
this.xfer = new SFTPFileTransfer(sftp);
public SFTPClient(SFTPEngine engine) {
this.engine = engine;
this.pathHelper = new PathHelper(engine);
this.xfer = new SFTPFileTransfer(engine);
}
public SFTPEngine getSFTPEngine() {
return sftp;
return engine;
}
public SFTPFileTransfer getFileTansfer() {
@@ -60,7 +58,7 @@ public class SFTPClient
public List<RemoteResourceInfo> ls(String path, RemoteResourceFilter filter)
throws IOException {
final RemoteDirectory dir = sftp.openDir(path);
final RemoteDirectory dir = engine.openDir(path);
try {
return dir.scan(filter);
} finally {
@@ -71,7 +69,7 @@ public class SFTPClient
public RemoteFile open(String filename, Set<OpenMode> mode, FileAttributes attrs)
throws IOException {
log.debug("Opening `{}`", filename);
return sftp.open(filename, mode, attrs);
return engine.open(filename, mode, attrs);
}
public RemoteFile open(String filename, Set<OpenMode> mode)
@@ -86,7 +84,7 @@ public class SFTPClient
public void mkdir(String dirname)
throws IOException {
sftp.makeDir(dirname);
engine.makeDir(dirname);
}
public void mkdirs(String path)
@@ -111,7 +109,7 @@ public class SFTPClient
public FileAttributes statExistence(String path)
throws IOException {
try {
return sftp.stat(path);
return engine.stat(path);
} catch (SFTPException sftpe) {
if (sftpe.getStatusCode() == Response.StatusCode.NO_SUCH_FILE) {
return null;
@@ -123,31 +121,31 @@ public class SFTPClient
public void rename(String oldpath, String newpath)
throws IOException {
sftp.rename(oldpath, newpath);
engine.rename(oldpath, newpath);
}
public void rm(String filename)
throws IOException {
sftp.remove(filename);
engine.remove(filename);
}
public void rmdir(String dirname)
throws IOException {
sftp.removeDir(dirname);
engine.removeDir(dirname);
}
public void symlink(String linkpath, String targetpath)
throws IOException {
sftp.symlink(linkpath, targetpath);
engine.symlink(linkpath, targetpath);
}
public int version() {
return sftp.getOperativeProtocolVersion();
return engine.getOperativeProtocolVersion();
}
public void setattr(String path, FileAttributes attrs)
throws IOException {
sftp.setAttributes(path, attrs);
engine.setAttributes(path, attrs);
}
public int uid(String path)
@@ -187,17 +185,17 @@ public class SFTPClient
public String readlink(String path)
throws IOException {
return sftp.readLink(path);
return engine.readLink(path);
}
public FileAttributes stat(String path)
throws IOException {
return sftp.stat(path);
return engine.stat(path);
}
public FileAttributes lstat(String path)
throws IOException {
return sftp.lstat(path);
return engine.lstat(path);
}
public void chown(String path, int uid)
@@ -222,7 +220,7 @@ public class SFTPClient
public String canonicalize(String path)
throws IOException {
return sftp.canonicalize(path);
return engine.canonicalize(path);
}
public long size(String path)
@@ -243,7 +241,7 @@ public class SFTPClient
@Override
public void close()
throws IOException {
sftp.close();
engine.close();
}
}

View File

@@ -33,37 +33,41 @@ import java.util.concurrent.TimeUnit;
public class SFTPEngine
implements Requester, Closeable {
/** Logger */
private final Logger log = LoggerFactory.getLogger(getClass());
public static final int PROTOCOL_VERSION = 3;
public static final int MAX_SUPPORTED_VERSION = 3;
public static final int DEFAULT_TIMEOUT = 30;
private volatile int timeout = DEFAULT_TIMEOUT;
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
private final Subsystem sub;
private final PacketReader reader;
private final OutputStream out;
protected volatile int timeout = DEFAULT_TIMEOUT;
private long reqID;
private int negotiatedVersion;
private final Map<String, String> serverExtensions = new HashMap<String, String>();
protected final int clientVersion;
protected final Subsystem sub;
protected final PacketReader reader;
protected final OutputStream out;
protected long reqID;
protected int operativeVersion;
protected final Map<String, String> serverExtensions = new HashMap<String, String>();
public SFTPEngine(SessionFactory ssh)
throws SSHException {
sub = ssh.startSession().startSubsystem("sftp");
out = sub.getOutputStream();
reader = new PacketReader(sub.getInputStream());
this(ssh, MAX_SUPPORTED_VERSION);
}
public Subsystem getSubsystem() {
return sub;
public SFTPEngine(SessionFactory ssh, int clientVersion)
throws SSHException {
if (clientVersion > MAX_SUPPORTED_VERSION)
throw new SFTPException("Max. supported protocol version is: " + MAX_SUPPORTED_VERSION);
this.clientVersion = clientVersion;
sub = ssh.startSession().startSubsystem("sftp");
out = sub.getOutputStream();
reader = new PacketReader(this);
}
public SFTPEngine init()
throws IOException {
transmit(new SFTPPacket<Request>(PacketType.INIT).putInt(PROTOCOL_VERSION));
transmit(new SFTPPacket<Request>(PacketType.INIT).putInt(clientVersion));
final SFTPPacket<Response> response = reader.readPacket();
@@ -71,10 +75,10 @@ public class SFTPEngine
if (type != PacketType.VERSION)
throw new SFTPException("Expected INIT packet, received: " + type);
negotiatedVersion = response.readInt();
log.info("Client version {}, server version {}", PROTOCOL_VERSION, negotiatedVersion);
if (negotiatedVersion < PROTOCOL_VERSION)
throw new SFTPException("Server reported protocol version: " + negotiatedVersion);
operativeVersion = response.readInt();
log.info("Client version {}, server version {}", clientVersion, operativeVersion);
if (operativeVersion < clientVersion)
throw new SFTPException("Server reported protocol version: " + operativeVersion);
while (response.available() > 0)
serverExtensions.put(response.readString(), response.readString());
@@ -84,8 +88,16 @@ public class SFTPEngine
return this;
}
public Subsystem getSubsystem() {
return sub;
}
public int getOperativeProtocolVersion() {
return negotiatedVersion;
return operativeVersion;
}
public Request newExtendedRequest(String reqName) {
return newRequest(PacketType.EXTENDED).putString(reqName);
}
@Override
@@ -102,17 +114,6 @@ public class SFTPEngine
return req.getResponseFuture().get(timeout, TimeUnit.SECONDS);
}
private synchronized void transmit(SFTPPacket<Request> payload)
throws IOException {
final int len = payload.available();
out.write((len >>> 24) & 0xff);
out.write((len >>> 16) & 0xff);
out.write((len >>> 8) & 0xff);
out.write(len & 0xff);
out.write(payload.array(), payload.rpos(), len);
out.flush();
}
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
throws IOException {
final String handle = doRequest(
@@ -148,6 +149,8 @@ public class SFTPEngine
public String readLink(String path)
throws IOException {
if (operativeVersion < 3)
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
return readSingleName(
doRequest(
newRequest(PacketType.READLINK).putString(path)
@@ -166,6 +169,8 @@ public class SFTPEngine
public void symlink(String linkpath, String targetpath)
throws IOException {
if (operativeVersion < 3)
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
doRequest(
newRequest(PacketType.SYMLINK).putString(linkpath).putString(targetpath)
).ensureStatusPacketIsOK();
@@ -185,13 +190,6 @@ public class SFTPEngine
).ensureStatusIs(Response.StatusCode.OK);
}
private FileAttributes stat(PacketType pt, String path)
throws IOException {
return doRequest(newRequest(pt).putString(path))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
public FileAttributes stat(String path)
throws IOException {
return stat(PacketType.STAT, path);
@@ -204,6 +202,8 @@ public class SFTPEngine
public void rename(String oldPath, String newPath)
throws IOException {
if (operativeVersion < 1)
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
doRequest(
newRequest(PacketType.RENAME).putString(oldPath).putString(newPath)
).ensureStatusPacketIsOK();
@@ -217,15 +217,6 @@ public class SFTPEngine
));
}
private static String readSingleName(Response res)
throws IOException {
res.ensurePacketTypeIs(PacketType.NAME);
if (res.readInt() == 1)
return res.readString();
else
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
@@ -240,4 +231,31 @@ public class SFTPEngine
sub.close();
}
protected FileAttributes stat(PacketType pt, String path)
throws IOException {
return doRequest(newRequest(pt).putString(path))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
protected static String readSingleName(Response res)
throws IOException {
res.ensurePacketTypeIs(PacketType.NAME);
if (res.readInt() == 1)
return res.readString();
else
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
}
protected synchronized void transmit(SFTPPacket<Request> payload)
throws IOException {
final int len = payload.available();
out.write((len >>> 24) & 0xff);
out.write((len >>> 16) & 0xff);
out.write((len >>> 8) & 0xff);
out.write(len & 0xff);
out.write(payload.array(), payload.rpos(), len);
out.flush();
}
}

View File

@@ -67,7 +67,6 @@ public class SFTPException
public StatusCode getStatusCode() {
return (sc == null) ? StatusCode.UNKNOWN : sc;
}
public SFTPException(StatusCode sc, String msg) {

View File

@@ -33,7 +33,7 @@ public class SFTPFileTransfer
extends AbstractFileTransfer
implements FileTransfer {
private final SFTPEngine sftp;
private final SFTPEngine engine;
private final PathHelper pathHelper;
private volatile FileFilter uploadFilter = defaultLocalFilter;
@@ -53,9 +53,9 @@ public class SFTPFileTransfer
}
};
public SFTPFileTransfer(SFTPEngine sftp) {
this.sftp = sftp;
this.pathHelper = new PathHelper(sftp);
public SFTPFileTransfer(SFTPEngine engine) {
this.engine = engine;
this.pathHelper = new PathHelper(engine);
}
@Override
@@ -68,7 +68,7 @@ public class SFTPFileTransfer
public void download(String source, String dest)
throws IOException {
final PathComponents pathComponents = pathHelper.getComponents(source);
final FileAttributes attributes = sftp.stat(source);
final FileAttributes attributes = engine.stat(source);
new Downloader().download(new RemoteResourceInfo(pathComponents, attributes), new File(dest));
}
@@ -119,7 +119,7 @@ public class SFTPFileTransfer
private File downloadDir(final RemoteResourceInfo remote, final File local)
throws IOException {
final File adjusted = FileTransferUtil.getTargetDirectory(local, remote.getName());
final RemoteDirectory rd = sftp.openDir(remote.getPath());
final RemoteDirectory rd = engine.openDir(remote.getPath());
try {
for (RemoteResourceInfo rri : rd.scan(getDownloadFilter()))
download(rri, new File(adjusted.getPath(), rri.getName()));
@@ -132,11 +132,11 @@ public class SFTPFileTransfer
private File downloadFile(final RemoteResourceInfo remote, final File local)
throws IOException {
final File adjusted = FileTransferUtil.getTargetFile(local, remote.getName());
final RemoteFile rf = sftp.open(remote.getPath());
final RemoteFile rf = engine.open(remote.getPath());
try {
final FileOutputStream fos = new FileOutputStream(adjusted);
try {
StreamCopier.copy(rf.getInputStream(), fos, sftp.getSubsystem()
StreamCopier.copy(rf.getInputStream(), fos, engine.getSubsystem()
.getLocalMaxPacketSize(), false, listener);
} finally {
fos.close();
@@ -176,7 +176,7 @@ public class SFTPFileTransfer
listener.finishedFile();
} else
throw new IOException(local + " is not a file or directory");
sftp.setAttributes(adjustedPath, getAttributes(local));
engine.setAttributes(adjustedPath, getAttributes(local));
}
private String uploadDir(File local, String remote)
@@ -190,13 +190,13 @@ public class SFTPFileTransfer
private String uploadFile(File local, String remote)
throws IOException {
final String adjusted = prepareFile(local, remote);
final RemoteFile rf = sftp.open(adjusted, EnumSet.of(OpenMode.WRITE,
final RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE,
OpenMode.CREAT,
OpenMode.TRUNC));
try {
final FileInputStream fis = new FileInputStream(local);
try {
final int bufSize = sftp.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
final int bufSize = engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
StreamCopier.copy(fis, rf.getOutputStream(), bufSize, false, listener);
} finally {
fis.close();
@@ -211,11 +211,11 @@ public class SFTPFileTransfer
throws IOException {
final FileAttributes attrs;
try {
attrs = sftp.stat(remote);
attrs = engine.stat(remote);
} catch (SFTPException e) {
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
log.debug("probeDir: {} does not exist, creating", remote);
sftp.makeDir(remote);
engine.makeDir(remote);
return remote;
} else
throw e;
@@ -237,7 +237,7 @@ public class SFTPFileTransfer
throws IOException {
final FileAttributes attrs;
try {
attrs = sftp.stat(remote);
attrs = engine.stat(remote);
} catch (SFTPException e) {
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
log.debug("probeFile: {} does not exist", remote);

View File

@@ -15,8 +15,6 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import java.io.IOException;
import java.util.List;
import java.util.Set;
@@ -26,9 +24,9 @@ public class StatefulSFTPClient
private String cwd;
public StatefulSFTPClient(SessionFactory ssh)
public StatefulSFTPClient(SFTPEngine engine)
throws IOException {
super(ssh);
super(engine);
this.cwd = getSFTPEngine().canonicalize(".");
log.info("Start dir = " + cwd);
}