diff options
author | Kenny Root <kenny@the-b.org> | 2015-07-24 16:13:51 -0700 |
---|---|---|
committer | Kenny Root <kenny@the-b.org> | 2015-07-24 16:13:51 -0700 |
commit | d3c4f49d68ba97f43aacbe86d4ece7546eee4f15 (patch) | |
tree | 6cfe6093f69737af10bc5437acc10942795f2b57 /app/src/main/java/com/trilead/ssh2/SFTPv3Client.java | |
parent | 739337624a5e69221a998cf10b1fd34fcc5ecd2d (diff) | |
parent | 571d61b6b55bc3eb9540e17973d93cc15b22da23 (diff) | |
download | connectbot-d3c4f49d68ba97f43aacbe86d4ece7546eee4f15.tar.gz connectbot-d3c4f49d68ba97f43aacbe86d4ece7546eee4f15.tar.bz2 connectbot-d3c4f49d68ba97f43aacbe86d4ece7546eee4f15.zip |
Merge pull request #105 from kruton/master
Update to library-based build
Diffstat (limited to 'app/src/main/java/com/trilead/ssh2/SFTPv3Client.java')
-rw-r--r-- | app/src/main/java/com/trilead/ssh2/SFTPv3Client.java | 1388 |
1 files changed, 0 insertions, 1388 deletions
diff --git a/app/src/main/java/com/trilead/ssh2/SFTPv3Client.java b/app/src/main/java/com/trilead/ssh2/SFTPv3Client.java deleted file mode 100644 index 06796e9..0000000 --- a/app/src/main/java/com/trilead/ssh2/SFTPv3Client.java +++ /dev/null @@ -1,1388 +0,0 @@ - -package com.trilead.ssh2; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Vector; - -import com.trilead.ssh2.packets.TypesReader; -import com.trilead.ssh2.packets.TypesWriter; -import com.trilead.ssh2.sftp.AttribFlags; -import com.trilead.ssh2.sftp.ErrorCodes; -import com.trilead.ssh2.sftp.Packet; - - -/** - * A <code>SFTPv3Client</code> represents a SFTP (protocol version 3) - * client connection tunnelled over a SSH-2 connection. This is a very simple - * (synchronous) implementation. - * <p> - * Basically, most methods in this class map directly to one of - * the packet types described in draft-ietf-secsh-filexfer-02.txt. - * <p> - * Note: this is experimental code. - * <p> - * Error handling: the methods of this class throw IOExceptions. However, unless - * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will - * be thrown (a subclass of IOException). Therefore, you can implement more verbose - * behavior by checking if a thrown exception if of this type. If yes, then you - * can cast the exception and access detailed information about the failure. - * <p> - * Notes about file names, directory names and paths, copy-pasted - * from the specs: - * <ul> - * <li>SFTP v3 represents file names as strings. File names are - * assumed to use the slash ('/') character as a directory separator.</li> - * <li>File names starting with a slash are "absolute", and are relative to - * the root of the file system. Names starting with any other character - * are relative to the user's default directory (home directory).</li> - * <li>Servers SHOULD interpret a path name component ".." as referring to - * the parent directory, and "." as referring to the current directory. - * If the server implementation limits access to certain parts of the - * file system, it must be extra careful in parsing file names when - * enforcing such restrictions. There have been numerous reported - * security bugs where a ".." in a path name has allowed access outside - * the intended area.</li> - * <li>An empty path name is valid, and it refers to the user's default - * directory (usually the user's home directory).</li> - * </ul> - * <p> - * If you are still not tired then please go on and read the comment for - * {@link #setCharset(String)}. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $ - */ -public class SFTPv3Client -{ - final Connection conn; - final Session sess; - final PrintStream debug; - - boolean flag_closed = false; - - InputStream is; - OutputStream os; - - int protocol_version = 0; - HashMap server_extensions = new HashMap(); - - int next_request_id = 1000; - - String charsetName = null; - - /** - * Create a SFTP v3 client. - * - * @param conn The underlying SSH-2 connection to be used. - * @param debug - * @throws IOException - * - * @deprecated this constructor (debug version) will disappear in the future, - * use {@link #SFTPv3Client(Connection)} instead. - */ - public SFTPv3Client(Connection conn, PrintStream debug) throws IOException - { - if (conn == null) - throw new IllegalArgumentException("Cannot accept null argument!"); - - this.conn = conn; - this.debug = debug; - - if (debug != null) - debug.println("Opening session and starting SFTP subsystem."); - - sess = conn.openSession(); - sess.startSubSystem("sftp"); - - is = sess.getStdout(); - os = new BufferedOutputStream(sess.getStdin(), 2048); - - if ((is == null) || (os == null)) - throw new IOException("There is a problem with the streams of the underlying channel."); - - init(); - } - - /** - * Create a SFTP v3 client. - * - * @param conn The underlying SSH-2 connection to be used. - * @throws IOException - */ - public SFTPv3Client(Connection conn) throws IOException - { - this(conn, null); - } - - /** - * Set the charset used to convert between Java Unicode Strings and byte encodings - * used by the server for paths and file names. Unfortunately, the SFTP v3 draft - * says NOTHING about such conversions (well, with the exception of error messages - * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names - * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3 - * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with - * filenames containing german umlauts). "windows-1252" seems to work better for Europe. - * Luckily, "windows-1252" is the platform default in my case =). - * <p> - * If you don't set anything, then the platform default will be used (this is the default - * behavior). - * - * @see #getCharset() - * - * @param charset the name of the charset to be used or <code>null</code> to use the platform's - * default encoding. - * @throws IOException - */ - public void setCharset(String charset) throws IOException - { - if (charset == null) - { - charsetName = charset; - return; - } - - try - { - Charset.forName(charset); - } - catch (Exception e) - { - throw (IOException) new IOException("This charset is not supported").initCause(e); - } - charsetName = charset; - } - - /** - * The currently used charset for filename encoding/decoding. - * - * @see #setCharset(String) - * - * @return The name of the charset (<code>null</code> if the platform's default charset is being used) - */ - public String getCharset() - { - return charsetName; - } - - private final void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException - { - if (handle.client != this) - throw new IOException("The file handle was created with another SFTPv3FileHandle instance."); - - if (handle.isClosed == true) - throw new IOException("The file handle is closed."); - } - - private final void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException - { - int msglen = len + 1; - - if (type != Packet.SSH_FXP_INIT) - msglen += 4; - - os.write(msglen >> 24); - os.write(msglen >> 16); - os.write(msglen >> 8); - os.write(msglen); - os.write(type); - - if (type != Packet.SSH_FXP_INIT) - { - os.write(requestId >> 24); - os.write(requestId >> 16); - os.write(requestId >> 8); - os.write(requestId); - } - - os.write(msg, off, len); - os.flush(); - } - - private final void sendMessage(int type, int requestId, byte[] msg) throws IOException - { - sendMessage(type, requestId, msg, 0, msg.length); - } - - private final void readBytes(byte[] buff, int pos, int len) throws IOException - { - while (len > 0) - { - int count = is.read(buff, pos, len); - if (count < 0) - throw new IOException("Unexpected end of sftp stream."); - if ((count == 0) || (count > len)) - throw new IOException("Underlying stream implementation is bogus!"); - len -= count; - pos += count; - } - } - - /** - * Read a message and guarantee that the <b>contents</b> is not larger than - * <code>maxlen</code> bytes. - * <p> - * Note: receiveMessage(34000) actually means that the message may be up to 34004 - * bytes (the length attribute preceeding the contents is 4 bytes). - * - * @param maxlen - * @return the message contents - * @throws IOException - */ - private final byte[] receiveMessage(int maxlen) throws IOException - { - byte[] msglen = new byte[4]; - - readBytes(msglen, 0, 4); - - int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff)); - - if ((len > maxlen) || (len <= 0)) - throw new IOException("Illegal sftp packet len: " + len); - - byte[] msg = new byte[len]; - - readBytes(msg, 0, len); - - return msg; - } - - private final int generateNextRequestID() - { - synchronized (this) - { - return next_request_id++; - } - } - - private final void closeHandle(byte[] handle) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(handle, 0, handle.length); - - sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException - { - /* - * uint32 flags - * uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE - * uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID - * uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID - * uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS - * uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME - * uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME - * uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED - * string extended_type - * string extended_data - * ... more extended data (extended_type - extended_data pairs), - * so that number of pairs equals extended_count - */ - - SFTPv3FileAttributes fa = new SFTPv3FileAttributes(); - - int flags = tr.readUINT32(); - - if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) - { - if (debug != null) - debug.println("SSH_FILEXFER_ATTR_SIZE"); - fa.size = new Long(tr.readUINT64()); - } - - if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0) - { - if (debug != null) - debug.println("SSH_FILEXFER_ATTR_V3_UIDGID"); - fa.uid = new Integer(tr.readUINT32()); - fa.gid = new Integer(tr.readUINT32()); - } - - if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) - { - if (debug != null) - debug.println("SSH_FILEXFER_ATTR_PERMISSIONS"); - fa.permissions = new Integer(tr.readUINT32()); - } - - if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0) - { - if (debug != null) - debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME"); - fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl); - fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl); - - } - - if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) - { - int count = tr.readUINT32(); - - if (debug != null) - debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")"); - - /* Read it anyway to detect corrupt packets */ - - while (count > 0) - { - tr.readByteString(); - tr.readByteString(); - count--; - } - } - - return fa; - } - - /** - * Retrieve the file attributes of an open file. - * - * @param handle a SFTPv3FileHandle handle. - * @return a SFTPv3FileAttributes object. - * @throws IOException - */ - public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException - { - checkHandleValidAndOpen(handle); - - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); - - if (debug != null) - { - debug.println("Sending SSH_FXP_FSTAT..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - if (debug != null) - { - debug.println("Got REPLY."); - debug.flush(); - } - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_ATTRS) - { - return readAttrs(tr); - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - throw new SFTPException(tr.readString(), errorCode); - } - - private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(path, charsetName); - - if (debug != null) - { - debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT..."); - debug.flush(); - } - - sendMessage(statMethod, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - if (debug != null) - { - debug.println("Got REPLY."); - debug.flush(); - } - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_ATTRS) - { - return readAttrs(tr); - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - throw new SFTPException(tr.readString(), errorCode); - } - - /** - * Retrieve the file attributes of a file. This method - * follows symbolic links on the server. - * - * @see #lstat(String) - * - * @param path See the {@link SFTPv3Client comment} for the class for more details. - * @return a SFTPv3FileAttributes object. - * @throws IOException - */ - public SFTPv3FileAttributes stat(String path) throws IOException - { - return statBoth(path, Packet.SSH_FXP_STAT); - } - - /** - * Retrieve the file attributes of a file. This method - * does NOT follow symbolic links on the server. - * - * @see #stat(String) - * - * @param path See the {@link SFTPv3Client comment} for the class for more details. - * @return a SFTPv3FileAttributes object. - * @throws IOException - */ - public SFTPv3FileAttributes lstat(String path) throws IOException - { - return statBoth(path, Packet.SSH_FXP_LSTAT); - } - - /** - * Read the target of a symbolic link. - * - * @param path See the {@link SFTPv3Client comment} for the class for more details. - * @return The target of the link. - * @throws IOException - */ - public String readLink(String path) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(path, charsetName); - - if (debug != null) - { - debug.println("Sending SSH_FXP_READLINK..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - if (debug != null) - { - debug.println("Got REPLY."); - debug.flush(); - } - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_NAME) - { - int count = tr.readUINT32(); - - if (count != 1) - throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); - - return tr.readString(charsetName); - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - throw new SFTPException(tr.readString(), errorCode); - } - - private void expectStatusOKMessage(int id) throws IOException - { - byte[] resp = receiveMessage(34000); - - if (debug != null) - { - debug.println("Got REPLY."); - debug.flush(); - } - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != id) - throw new IOException("The server sent an invalid id field."); - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - if (errorCode == ErrorCodes.SSH_FX_OK) - return; - - throw new SFTPException(tr.readString(), errorCode); - } - - /** - * Modify the attributes of a file. Used for operations such as changing - * the ownership, permissions or access times, as well as for truncating a file. - * - * @param path See the {@link SFTPv3Client comment} for the class for more details. - * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be - * made to the attributes of the file. Empty fields will be ignored. - * @throws IOException - */ - public void setstat(String path, SFTPv3FileAttributes attr) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(path, charsetName); - tw.writeBytes(createAttrs(attr)); - - if (debug != null) - { - debug.println("Sending SSH_FXP_SETSTAT..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Modify the attributes of a file. Used for operations such as changing - * the ownership, permissions or access times, as well as for truncating a file. - * - * @param handle a SFTPv3FileHandle handle - * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be - * made to the attributes of the file. Empty fields will be ignored. - * @throws IOException - */ - public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException - { - checkHandleValidAndOpen(handle); - - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); - tw.writeBytes(createAttrs(attr)); - - if (debug != null) - { - debug.println("Sending SSH_FXP_FSETSTAT..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Create a symbolic link on the server. Creates a link "src" that points - * to "target". - * - * @param src See the {@link SFTPv3Client comment} for the class for more details. - * @param target See the {@link SFTPv3Client comment} for the class for more details. - * @throws IOException - */ - public void createSymlink(String src, String target) throws IOException - { - int req_id = generateNextRequestID(); - - /* Either I am too stupid to understand the SFTP draft - * or the OpenSSH guys changed the semantics of src and target. - */ - - TypesWriter tw = new TypesWriter(); - tw.writeString(target, charsetName); - tw.writeString(src, charsetName); - - if (debug != null) - { - debug.println("Sending SSH_FXP_SYMLINK..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Have the server canonicalize any given path name to an absolute path. - * This is useful for converting path names containing ".." components or - * relative pathnames without a leading slash into absolute paths. - * - * @param path See the {@link SFTPv3Client comment} for the class for more details. - * @return An absolute path. - * @throws IOException - */ - public String canonicalPath(String path) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(path, charsetName); - - if (debug != null) - { - debug.println("Sending SSH_FXP_REALPATH..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - if (debug != null) - { - debug.println("Got REPLY."); - debug.flush(); - } - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_NAME) - { - int count = tr.readUINT32(); - - if (count != 1) - throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); - - return tr.readString(charsetName); - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - throw new SFTPException(tr.readString(), errorCode); - } - - private final Vector scanDirectory(byte[] handle) throws IOException - { - Vector files = new Vector(); - - while (true) - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(handle, 0, handle.length); - - if (debug != null) - { - debug.println("Sending SSH_FXP_READDIR..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes()); - - /* Some servers send here a packet with size > 34000 */ - /* To whom it may concern: please learn to read the specs. */ - - byte[] resp = receiveMessage(65536); - - if (debug != null) - { - debug.println("Got REPLY."); - debug.flush(); - } - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_NAME) - { - int count = tr.readUINT32(); - - if (debug != null) - debug.println("Parsing " + count + " name entries..."); - - while (count > 0) - { - SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry(); - - dirEnt.filename = tr.readString(charsetName); - dirEnt.longEntry = tr.readString(charsetName); - - dirEnt.attributes = readAttrs(tr); - files.addElement(dirEnt); - - if (debug != null) - debug.println("File: '" + dirEnt.filename + "'"); - count--; - } - continue; - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - if (errorCode == ErrorCodes.SSH_FX_EOF) - return files; - - throw new SFTPException(tr.readString(), errorCode); - } - } - - private final byte[] openDirectory(String path) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(path, charsetName); - - if (debug != null) - { - debug.println("Sending SSH_FXP_OPENDIR..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_HANDLE) - { - if (debug != null) - { - debug.println("Got SSH_FXP_HANDLE."); - debug.flush(); - } - - byte[] handle = tr.readByteString(); - return handle; - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - String errorMessage = tr.readString(); - - throw new SFTPException(errorMessage, errorCode); - } - - private final String expandString(byte[] b, int off, int len) - { - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < len; i++) - { - int c = b[off + i] & 0xff; - - if ((c >= 32) && (c <= 126)) - { - sb.append((char) c); - } - else - { - sb.append("{0x" + Integer.toHexString(c) + "}"); - } - } - - return sb.toString(); - } - - private void init() throws IOException - { - /* Send SSH_FXP_INIT (version 3) */ - - final int client_version = 3; - - if (debug != null) - debug.println("Sending SSH_FXP_INIT (" + client_version + ")..."); - - TypesWriter tw = new TypesWriter(); - tw.writeUINT32(client_version); - sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes()); - - /* Receive SSH_FXP_VERSION */ - - if (debug != null) - debug.println("Waiting for SSH_FXP_VERSION..."); - - TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */ - - int type = tr.readByte(); - - if (type != Packet.SSH_FXP_VERSION) - { - throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")"); - } - - protocol_version = tr.readUINT32(); - - if (debug != null) - debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version); - - if (protocol_version != 3) - throw new IOException("Server version " + protocol_version + " is currently not supported"); - - /* Read and save extensions (if any) for later use */ - - while (tr.remain() != 0) - { - String name = tr.readString(); - byte[] value = tr.readByteString(); - server_extensions.put(name, value); - - if (debug != null) - debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length) - + "'"); - } - } - - /** - * Returns the negotiated SFTP protocol version between the client and the server. - * - * @return SFTP protocol version, i.e., "3". - * - */ - public int getProtocolVersion() - { - return protocol_version; - } - - /** - * Close this SFTP session. NEVER forget to call this method to free up - * resources - even if you got an exception from one of the other methods. - * Sometimes these other methods may throw an exception, saying that the - * underlying channel is closed (this can happen, e.g., if the other server - * sent a close message.) However, as long as you have not called the - * <code>close()</code> method, you are likely wasting resources. - * - */ - public void close() - { - sess.close(); - } - - /** - * List the contents of a directory. - * - * @param dirName See the {@link SFTPv3Client comment} for the class for more details. - * @return A Vector containing {@link SFTPv3DirectoryEntry} objects. - * @throws IOException - */ - public Vector ls(String dirName) throws IOException - { - byte[] handle = openDirectory(dirName); - Vector result = scanDirectory(handle); - closeHandle(handle); - return result; - } - - /** - * Create a new directory. - * - * @param dirName See the {@link SFTPv3Client comment} for the class for more details. - * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that - * this is octal noation). The server will likely apply a umask. - * - * @throws IOException - */ - public void mkdir(String dirName, int posixPermissions) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(dirName, charsetName); - tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS); - tw.writeUINT32(posixPermissions); - - sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Remove a file. - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @throws IOException - */ - public void rm(String fileName) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(fileName, charsetName); - - sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Remove an empty directory. - * - * @param dirName See the {@link SFTPv3Client comment} for the class for more details. - * @throws IOException - */ - public void rmdir(String dirName) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(dirName, charsetName); - - sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Move a file or directory. - * - * @param oldPath See the {@link SFTPv3Client comment} for the class for more details. - * @param newPath See the {@link SFTPv3Client comment} for the class for more details. - * @throws IOException - */ - public void mv(String oldPath, String newPath) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(oldPath, charsetName); - tw.writeString(newPath, charsetName); - - sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes()); - - expectStatusOKMessage(req_id); - } - - /** - * Open a file for reading. - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @return a SFTPv3FileHandle handle - * @throws IOException - */ - public SFTPv3FileHandle openFileRO(String fileName) throws IOException - { - return openFile(fileName, 0x00000001, null); // SSH_FXF_READ - } - - /** - * Open a file for reading and writing. - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @return a SFTPv3FileHandle handle - * @throws IOException - */ - public SFTPv3FileHandle openFileRW(String fileName) throws IOException - { - return openFile(fileName, 0x00000003, null); // SSH_FXF_READ | SSH_FXF_WRITE - } - - // Append is broken (already in the specification, because there is no way to - // send a write operation (what offset to use??)) - // public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException - // { - // return openFile(fileName, 0x00000007, null); // SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND - // } - - /** - * Create a file and open it for reading and writing. - * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}. - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @return a SFTPv3FileHandle handle - * @throws IOException - */ - public SFTPv3FileHandle createFile(String fileName) throws IOException - { - return createFile(fileName, null); - } - - /** - * Create a file and open it for reading and writing. - * You can specify the default attributes of the file (the server may or may - * not respect your wishes). - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @param attr may be <code>null</code> to use server defaults. Probably only - * the <code>uid</code>, <code>gid</code> and <code>permissions</code> - * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} - * structure make sense. You need only to set those fields where you want - * to override the server's defaults. - * @return a SFTPv3FileHandle handle - * @throws IOException - */ - public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException - { - return openFile(fileName, 0x00000008 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE - } - - /** - * Create a file (truncate it if it already exists) and open it for reading and writing. - * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}. - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @return a SFTPv3FileHandle handle - * @throws IOException - */ - public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException - { - return createFileTruncate(fileName, null); - } - - /** - * reate a file (truncate it if it already exists) and open it for reading and writing. - * You can specify the default attributes of the file (the server may or may - * not respect your wishes). - * - * @param fileName See the {@link SFTPv3Client comment} for the class for more details. - * @param attr may be <code>null</code> to use server defaults. Probably only - * the <code>uid</code>, <code>gid</code> and <code>permissions</code> - * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} - * structure make sense. You need only to set those fields where you want - * to override the server's defaults. - * @return a SFTPv3FileHandle handle - * @throws IOException - */ - public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException - { - return openFile(fileName, 0x00000018 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_READ | SSH_FXF_WRITE - } - - private byte[] createAttrs(SFTPv3FileAttributes attr) - { - TypesWriter tw = new TypesWriter(); - - int attrFlags = 0; - - if (attr == null) - { - tw.writeUINT32(0); - } - else - { - if (attr.size != null) - attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE; - - if ((attr.uid != null) && (attr.gid != null)) - attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID; - - if (attr.permissions != null) - attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS; - - if ((attr.atime != null) && (attr.mtime != null)) - attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME; - - tw.writeUINT32(attrFlags); - - if (attr.size != null) - tw.writeUINT64(attr.size.longValue()); - - if ((attr.uid != null) && (attr.gid != null)) - { - tw.writeUINT32(attr.uid.intValue()); - tw.writeUINT32(attr.gid.intValue()); - } - - if (attr.permissions != null) - tw.writeUINT32(attr.permissions.intValue()); - - if ((attr.atime != null) && (attr.mtime != null)) - { - tw.writeUINT32(attr.atime.intValue()); - tw.writeUINT32(attr.mtime.intValue()); - } - } - - return tw.getBytes(); - } - - private SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException - { - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(fileName, charsetName); - tw.writeUINT32(flags); - tw.writeBytes(createAttrs(attr)); - - if (debug != null) - { - debug.println("Sending SSH_FXP_OPEN..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_HANDLE) - { - if (debug != null) - { - debug.println("Got SSH_FXP_HANDLE."); - debug.flush(); - } - - return new SFTPv3FileHandle(this, tr.readByteString()); - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - String errorMessage = tr.readString(); - - throw new SFTPException(errorMessage, errorCode); - } - - /** - * Read bytes from a file. No more than 32768 bytes may be read at once. - * Be aware that the semantics of read() are different than for Java streams. - * <p> - * <ul> - * <li>The server will read as many bytes as it can from the file (up to <code>len</code>), - * and return them.</li> - * <li>If EOF is encountered before reading any data, <code>-1</code> is returned. - * <li>If an error occurs, an exception is thrown</li>. - * <li>For normal disk files, it is guaranteed that the server will return the specified - * number of bytes, or up to end of file. For, e.g., device files this may return - * fewer bytes than requested.</li> - * </ul> - * - * @param handle a SFTPv3FileHandle handle - * @param fileOffset offset (in bytes) in the file - * @param dst the destination byte array - * @param dstoff offset in the destination byte array - * @param len how many bytes to read, 0 < len <= 32768 bytes - * @return the number of bytes that could be read, may be less than requested if - * the end of the file is reached, -1 is returned in case of <code>EOF</code> - * @throws IOException - */ - public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException - { - checkHandleValidAndOpen(handle); - - if ((len > 32768) || (len <= 0)) - throw new IllegalArgumentException("invalid len argument"); - - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); - tw.writeUINT64(fileOffset); - tw.writeUINT32(len); - - if (debug != null) - { - debug.println("Sending SSH_FXP_READ..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes()); - - byte[] resp = receiveMessage(34000); - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t == Packet.SSH_FXP_DATA) - { - if (debug != null) - { - debug.println("Got SSH_FXP_DATA..."); - debug.flush(); - } - - int readLen = tr.readUINT32(); - - if ((readLen < 0) || (readLen > len)) - throw new IOException("The server sent an invalid length field."); - - tr.readBytes(dst, dstoff, readLen); - - return readLen; - } - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - if (errorCode == ErrorCodes.SSH_FX_EOF) - { - if (debug != null) - { - debug.println("Got SSH_FX_EOF."); - debug.flush(); - } - - return -1; - } - - String errorMessage = tr.readString(); - - throw new SFTPException(errorMessage, errorCode); - } - - /** - * Write bytes to a file. If <code>len</code> > 32768, then the write operation will - * be split into multiple writes. - * - * @param handle a SFTPv3FileHandle handle. - * @param fileOffset offset (in bytes) in the file. - * @param src the source byte array. - * @param srcoff offset in the source byte array. - * @param len how many bytes to write. - * @throws IOException - */ - public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException - { - checkHandleValidAndOpen(handle); - - while (len > 0) - { - int writeRequestLen = len; - - if (writeRequestLen > 32768) - writeRequestLen = 32768; - - int req_id = generateNextRequestID(); - - TypesWriter tw = new TypesWriter(); - tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); - tw.writeUINT64(fileOffset); - tw.writeString(src, srcoff, writeRequestLen); - - if (debug != null) - { - debug.println("Sending SSH_FXP_WRITE..."); - debug.flush(); - } - - sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes()); - - fileOffset += writeRequestLen; - - srcoff += writeRequestLen; - len -= writeRequestLen; - - byte[] resp = receiveMessage(34000); - - TypesReader tr = new TypesReader(resp); - - int t = tr.readByte(); - - int rep_id = tr.readUINT32(); - if (rep_id != req_id) - throw new IOException("The server sent an invalid id field."); - - if (t != Packet.SSH_FXP_STATUS) - throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); - - int errorCode = tr.readUINT32(); - - if (errorCode == ErrorCodes.SSH_FX_OK) - continue; - - String errorMessage = tr.readString(); - - throw new SFTPException(errorMessage, errorCode); - } - } - - /** - * Close a file. - * - * @param handle a SFTPv3FileHandle handle - * @throws IOException - */ - public void closeFile(SFTPv3FileHandle handle) throws IOException - { - if (handle == null) - throw new IllegalArgumentException("the handle argument may not be null"); - - try - { - if (handle.isClosed == false) - { - closeHandle(handle.fileHandle); - } - } - finally - { - handle.isClosed = true; - } - } -} |