148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/* 248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. 348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Please refer to the LICENSE.txt for licensing details. 448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodpackage ch.ethz.ssh2; 648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.BufferedOutputStream; 848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.IOException; 948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.InputStream; 1048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.OutputStream; 1148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.nio.charset.Charset; 1248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.nio.charset.UnsupportedCharsetException; 1348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.util.HashMap; 1448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.util.List; 1548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.util.Map; 1648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.util.Vector; 1748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 1848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.channel.Channel; 1948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.log.Logger; 2048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.packets.TypesReader; 2148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.packets.TypesWriter; 2248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.sftp.AttribFlags; 2348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.sftp.ErrorCodes; 2448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.sftp.Packet; 2548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 2648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/** 2748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * A <code>SFTPv3Client</code> represents a SFTP (protocol version 3) 2848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * client connection tunnelled over a SSH-2 connection. This is a very simple 2948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (synchronous) implementation. 3048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 3148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Basically, most methods in this class map directly to one of 3248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the packet types described in draft-ietf-secsh-filexfer-02.txt. 3348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 3448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Note: this is experimental code. 3548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 3648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Error handling: the methods of this class throw IOExceptions. However, unless 3748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will 3848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * be thrown (a subclass of IOException). Therefore, you can implement more verbose 3948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * behavior by checking if a thrown exception if of this type. If yes, then you 4048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * can cast the exception and access detailed information about the failure. 4148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 4248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Notes about file names, directory names and paths, copy-pasted 4348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * from the specs: 4448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <ul> 4548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>SFTP v3 represents file names as strings. File names are 4648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * assumed to use the slash ('/') character as a directory separator.</li> 4748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>File names starting with a slash are "absolute", and are relative to 4848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the root of the file system. Names starting with any other character 4948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * are relative to the user's default directory (home directory).</li> 5048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>Servers SHOULD interpret a path name component ".." as referring to 5148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the parent directory, and "." as referring to the current directory. 5248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * If the server implementation limits access to certain parts of the 5348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * file system, it must be extra careful in parsing file names when 5448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * enforcing such restrictions. There have been numerous reported 5548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * security bugs where a ".." in a path name has allowed access outside 5648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the intended area.</li> 5748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>An empty path name is valid, and it refers to the user's default 5848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * directory (usually the user's home directory).</li> 5948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * </ul> 6048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 6148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * If you are still not tired then please go on and read the comment for 6248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * {@link #setCharset(String)}. 6348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 6448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @author Christian Plattner, plattner@inf.ethz.ch 6548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @version $Id: SFTPv3Client.java 46 2011-07-06 08:40:29Z dkocher@sudo.ch $ 6648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 6748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodpublic class SFTPv3Client 6848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood{ 6948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static final Logger log = Logger.getLogger(SFTPv3Client.class); 7048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 7148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private Session sess; 7248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 7348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private InputStream is; 7448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private OutputStream os; 7548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 7648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private int protocol_version = 0; 7748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 7848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private int next_request_id = 1000; 7948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 8048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private String charsetName = null; 8148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 8248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 8348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 8448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 8548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private PacketListener listener; 8648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 8748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 8848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a SFTP v3 client. 8948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 9048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param conn The underlying SSH-2 connection to be used. 9148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 9248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 9348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3Client(Connection conn, PacketListener listener) throws IOException 9448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 9548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (conn == null) 9648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 9748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IllegalArgumentException("Cannot accept null argument!"); 9848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 9948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 10048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood this.listener = listener; 10148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 10248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Opening session and starting SFTP subsystem."); 10348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sess = conn.openSession(); 10448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sess.startSubSystem("sftp"); 10548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 10648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood is = sess.getStdout(); 10748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os = new BufferedOutputStream(sess.getStdin(), 2048); 10848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 10948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (is == null) 11048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 11148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("There is a problem with the streams of the underlying channel."); 11248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 11348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 11448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood init(); 11548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 11648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 11748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 11848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a SFTP v3 client. 11948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 12048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param conn The underlying SSH-2 connection to be used. 12148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 12248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 12348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3Client(Connection conn) throws IOException 12448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 12548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood this(conn, new PacketListener() 12648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 12748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void read(String packet) 12848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 12948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Read packet " + packet); 13048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 13148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 13248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void write(String packet) 13348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 13448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Write packet " + packet); 13548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 13648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood }); 13748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 13848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 13948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 14048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Set the charset used to convert between Java Unicode Strings and byte encodings 14148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * used by the server for paths and file names. Unfortunately, the SFTP v3 draft 14248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * says NOTHING about such conversions (well, with the exception of error messages 14348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names 14448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3 14548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with 14648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * filenames containing german umlauts). "windows-1252" seems to work better for Europe. 14748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Luckily, "windows-1252" is the platform default in my case =). 14848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 14948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * If you don't set anything, then the platform default will be used (this is the default 15048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * behavior). 15148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 15248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param charset the name of the charset to be used or <code>null</code> to use the platform's 15348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * default encoding. 15448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 15548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @see #getCharset() 15648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 15748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void setCharset(String charset) throws IOException 15848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 15948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (charset == null) 16048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 16148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood charsetName = charset; 16248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return; 16348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 16448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 16548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood try 16648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 16748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood Charset.forName(charset); 16848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 16948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood catch (UnsupportedCharsetException e) 17048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 17148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw (IOException) new IOException("This charset is not supported").initCause(e); 17248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 17348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood charsetName = charset; 17448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 17548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 17648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 17748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * The currently used charset for filename encoding/decoding. 17848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 17948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return The name of the charset (<code>null</code> if the platform's default charset is being used) 18048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @see #setCharset(String) 18148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 18248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public String getCharset() 18348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 18448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return charsetName; 18548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 18648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 18748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException 18848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 18948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (handle.client != this) 19048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 19148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The file handle was created with another SFTPv3FileHandle instance."); 19248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 19348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 19448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (handle.isClosed) 19548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 19648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The file handle is closed."); 19748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 19848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 19948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 20048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException 20148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 20248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.write(Packet.forName(type)); 20348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 20448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int msglen = len + 1; 20548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 20648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (type != Packet.SSH_FXP_INIT) 20748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 20848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood msglen += 4; 20948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 21048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 21148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(msglen >> 24); 21248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(msglen >> 16); 21348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(msglen >> 8); 21448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(msglen); 21548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(type); 21648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 21748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (type != Packet.SSH_FXP_INIT) 21848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 21948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(requestId >> 24); 22048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(requestId >> 16); 22148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(requestId >> 8); 22248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(requestId); 22348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 22448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 22548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.write(msg, off, len); 22648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood os.flush(); 22748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 22848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 22948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void sendMessage(int type, int requestId, byte[] msg) throws IOException 23048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 23148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(type, requestId, msg, 0, msg.length); 23248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 23348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 23448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void readBytes(byte[] buff, int pos, int len) throws IOException 23548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 23648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (len > 0) 23748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 23848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int count = is.read(buff, pos, len); 23948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (count < 0) 24048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 24148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Unexpected end of sftp stream."); 24248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 24348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((count == 0) || (count > len)) 24448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 24548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Underlying stream implementation is bogus!"); 24648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 24748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood len -= count; 24848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood pos += count; 24948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 25048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 25148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 25248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 25348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Read a message and guarantee that the <b>contents</b> is not larger than 25448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <code>maxlen</code> bytes. 25548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 25648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Note: receiveMessage(34000) actually means that the message may be up to 34004 25748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * bytes (the length attribute preceeding the contents is 4 bytes). 25848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 25948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param maxlen 26048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return the message contents 26148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 26248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 26348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private byte[] receiveMessage(int maxlen) throws IOException 26448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 26548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] msglen = new byte[4]; 26648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 26748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood readBytes(msglen, 0, 4); 26848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 26948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff)); 27048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 27148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((len > maxlen) || (len <= 0)) 27248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 27348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Illegal sftp packet len: " + len); 27448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 27548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 27648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] msg = new byte[len]; 27748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 27848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood readBytes(msg, 0, len); 27948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 28048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return msg; 28148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 28248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 28348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private int generateNextRequestID() 28448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 28548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood synchronized (this) 28648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 28748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return next_request_id++; 28848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 28948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 29048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 29148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void closeHandle(byte[] handle) throws IOException 29248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 29348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 29448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 29548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 29648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(handle, 0, handle.length); 29748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 29848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes()); 29948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 30048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 30148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 30248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 30348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException 30448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 30548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* 30648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 flags 30748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE 30848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID 30948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID 31048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS 31148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME 31248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME 31348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED 31448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * string extended_type 31548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * string extended_data 31648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * ... more extended data (extended_type - extended_data pairs), 31748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * so that number of pairs equals extended_count 31848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 31948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 32048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood SFTPv3FileAttributes fa = new SFTPv3FileAttributes(); 32148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 32248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int flags = tr.readUINT32(); 32348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 32448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) 32548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 32648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FILEXFER_ATTR_SIZE"); 32748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood fa.size = tr.readUINT64(); 32848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 32948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 33048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0) 33148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 33248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FILEXFER_ATTR_V3_UIDGID"); 33348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood fa.uid = tr.readUINT32(); 33448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood fa.gid = tr.readUINT32(); 33548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 33648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 33748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) 33848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 33948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FILEXFER_ATTR_PERMISSIONS"); 34048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood fa.permissions = tr.readUINT32(); 34148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 34248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 34348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0) 34448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 34548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FILEXFER_ATTR_V3_ACMODTIME"); 34648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood fa.atime = tr.readUINT32(); 34748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood fa.mtime = tr.readUINT32(); 34848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 34948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 35048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 35148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) 35248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 35348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int count = tr.readUINT32(); 35448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 35548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")"); 35648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Read it anyway to detect corrupt packets */ 35748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 35848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (count > 0) 35948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 36048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tr.readByteString(); 36148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tr.readByteString(); 36248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood count--; 36348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 36448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 36548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 36648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return fa; 36748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 36848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 36948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 37048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Retrieve the file attributes of an open file. 37148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 37248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param handle a SFTPv3FileHandle handle. 37348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileAttributes object. 37448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 37548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 37648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException 37748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 37848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood checkHandleValidAndOpen(handle); 37948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 38048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 38148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 38248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 38348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); 38448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 38548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_FSTAT..."); 38648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes()); 38748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 38848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 38948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 39048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 39148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 39248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 39348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 39448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 39548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 39648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 39748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 39848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 39948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 40048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 40148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_ATTRS) 40248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 40348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return readAttrs(tr); 40448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 40548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 40648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 40748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 40848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 40948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 41048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 41148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 41248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 41348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 41448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 41548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 41648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 41748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException 41848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 41948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 42048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 42148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 42248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(path, charsetName); 42348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 42448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_STAT/SSH_FXP_LSTAT..."); 42548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(statMethod, req_id, tw.getBytes()); 42648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 42748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 42848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 42948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 43048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 43148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 43248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 43348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 43448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 43548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 43648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 43748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 43848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 43948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 44048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_ATTRS) 44148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 44248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return readAttrs(tr); 44348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 44448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 44548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 44648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 44748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 44848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 44948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 45048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 45148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 45248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 45348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 45448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 45548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 45648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 45748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Retrieve the file attributes of a file. This method 45848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * follows symbolic links on the server. 45948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 46048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param path See the {@link SFTPv3Client comment} for the class for more details. 46148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileAttributes object. 46248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 46348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @see #lstat(String) 46448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 46548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileAttributes stat(String path) throws IOException 46648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 46748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return statBoth(path, Packet.SSH_FXP_STAT); 46848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 46948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 47048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 47148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Retrieve the file attributes of a file. This method 47248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * does NOT follow symbolic links on the server. 47348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 47448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param path See the {@link SFTPv3Client comment} for the class for more details. 47548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileAttributes object. 47648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 47748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @see #stat(String) 47848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 47948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileAttributes lstat(String path) throws IOException 48048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 48148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return statBoth(path, Packet.SSH_FXP_LSTAT); 48248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 48348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 48448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 48548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Read the target of a symbolic link. Note: OpenSSH (as of version 4.4) gets very upset 48648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (SSH_FX_BAD_MESSAGE error) if you want to read the target of a file that is not a 48748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * symbolic link. Better check first with {@link #lstat(String)}. 48848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 48948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param path See the {@link SFTPv3Client comment} for the class for more details. 49048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return The target of the link. 49148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 49248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 49348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public String readLink(String path) throws IOException 49448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 49548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 49648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 49748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 49848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(path, charsetName); 49948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 50048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_READLINK..."); 50148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes()); 50248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 50348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 50448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 50548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 50648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 50748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 50848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 50948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 51048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 51148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 51248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 51348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 51448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 51548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 51648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_NAME) 51748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 51848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int count = tr.readUINT32(); 51948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 52048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (count != 1) 52148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 52248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); 52348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 52448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 52548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return tr.readString(charsetName); 52648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 52748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 52848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 52948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 53048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 53148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 53248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 53348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 53448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 53548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 53648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 53748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 53848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 53948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void expectStatusOKMessage(int id) throws IOException 54048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 54148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 54248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 54348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 54448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 54548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 54648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 54748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 54848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 54948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != id) 55048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 55148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 55248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 55348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 55448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 55548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 55648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 55748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 55848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 55948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 56048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 56148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (errorCode == ErrorCodes.SSH_FX_OK) 56248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 56348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return; 56448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 56548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 56648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 56748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 56848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 56948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 57048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 57148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Modify the attributes of a file. Used for operations such as changing 57248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the ownership, permissions or access times, as well as for truncating a file. 57348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 57448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param path See the {@link SFTPv3Client comment} for the class for more details. 57548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be 57648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * made to the attributes of the file. Empty fields will be ignored. 57748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 57848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 57948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void setstat(String path, SFTPv3FileAttributes attr) throws IOException 58048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 58148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 58248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 58348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 58448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(path, charsetName); 58548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeBytes(createAttrs(attr)); 58648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 58748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_SETSTAT..."); 58848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes()); 58948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 59048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 59148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 59248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 59348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 59448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Modify the attributes of a file. Used for operations such as changing 59548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the ownership, permissions or access times, as well as for truncating a file. 59648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 59748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param handle a SFTPv3FileHandle handle 59848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be 59948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * made to the attributes of the file. Empty fields will be ignored. 60048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 60148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 60248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException 60348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 60448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood checkHandleValidAndOpen(handle); 60548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 60648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 60748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 60848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 60948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); 61048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeBytes(createAttrs(attr)); 61148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 61248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_FSETSTAT..."); 61348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes()); 61448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 61548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 61648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 61748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 61848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 61948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a symbolic link on the server. Creates a link "src" that points 62048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * to "target". 62148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 62248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param src See the {@link SFTPv3Client comment} for the class for more details. 62348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param target See the {@link SFTPv3Client comment} for the class for more details. 62448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 62548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 62648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void createSymlink(String src, String target) throws IOException 62748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 62848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 62948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 63048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Either I am too stupid to understand the SFTP draft 63148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * or the OpenSSH guys changed the semantics of src and target. 63248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 63348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 63448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 63548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(target, charsetName); 63648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(src, charsetName); 63748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 63848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_SYMLINK..."); 63948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes()); 64048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 64148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 64248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 64348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 64448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 64548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Have the server canonicalize any given path name to an absolute path. 64648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * This is useful for converting path names containing ".." components or 64748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * relative pathnames without a leading slash into absolute paths. 64848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 64948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param path See the {@link SFTPv3Client comment} for the class for more details. 65048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return An absolute path. 65148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 65248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 65348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public String canonicalPath(String path) throws IOException 65448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 65548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 65648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 65748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 65848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(path, charsetName); 65948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 66048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_REALPATH..."); 66148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes()); 66248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 66348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 66448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 66548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 66648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 66748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 66848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 66948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 67048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 67148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 67248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 67348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 67448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 67548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 67648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_NAME) 67748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 67848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int count = tr.readUINT32(); 67948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 68048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (count != 1) 68148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 68248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); 68348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 68448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 68548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood final String name = tr.readString(charsetName); 68648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(name); 68748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return name; 68848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 68948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 69048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 69148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 69248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 69348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 69448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 69548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 69648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 69748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 69848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 69948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 70048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 70148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private List<SFTPv3DirectoryEntry> scanDirectory(byte[] handle) throws IOException 70248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 70348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood List<SFTPv3DirectoryEntry> files = new Vector<SFTPv3DirectoryEntry>(); 70448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 70548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (true) 70648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 70748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 70848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 70948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 71048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(handle, 0, handle.length); 71148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 71248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_READDIR..."); 71348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes()); 71448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 71548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 71648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 71748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 71848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 71948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 72048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 72148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 72248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 72348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 72448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 72548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 72648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 72748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 72848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_NAME) 72948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 73048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int count = tr.readUINT32(); 73148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 73248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Parsing " + count + " name entries..."); 73348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (count > 0) 73448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 73548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry(); 73648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 73748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood dirEnt.filename = tr.readString(charsetName); 73848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood dirEnt.longEntry = tr.readString(charsetName); 73948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(dirEnt.longEntry); 74048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 74148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood dirEnt.attributes = readAttrs(tr); 74248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood files.add(dirEnt); 74348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 74448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("File: '" + dirEnt.filename + "'"); 74548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood count--; 74648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 74748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood continue; 74848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 74948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 75048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 75148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 75248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 75348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 75448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 75548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 75648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 75748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (errorCode == ErrorCodes.SSH_FX_EOF) 75848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 75948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return files; 76048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 76148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 76248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 76348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 76448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 76548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 76648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 76748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public final SFTPv3FileHandle openDirectory(String path) throws IOException 76848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 76948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 77048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 77148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 77248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(path, charsetName); 77348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 77448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_OPENDIR..."); 77548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes()); 77648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 77748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 77848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 77948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 78048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 78148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 78248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 78348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 78448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 78548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 78648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 78748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 78848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 78948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 79048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_HANDLE) 79148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 79248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Got SSH_FXP_HANDLE."); 79348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return new SFTPv3FileHandle(this, tr.readByteString()); 79448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 79548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 79648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 79748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 79848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 79948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 80048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 80148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 80248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 80348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 80448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 80548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 80648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 80748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private String expandString(byte[] b, int off, int len) 80848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 80948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood StringBuilder sb = new StringBuilder(); 81048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 81148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood for (int i = 0; i < len; i++) 81248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 81348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int c = b[off + i] & 0xff; 81448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 81548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((c >= 32) && (c <= 126)) 81648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 81748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sb.append((char) c); 81848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 81948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else 82048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 82148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sb.append("{0x" + Integer.toHexString(c) + "}"); 82248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 82348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 82448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 82548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return sb.toString(); 82648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 82748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 82848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void init() throws IOException 82948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 83048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Send SSH_FXP_INIT (version 3) */ 83148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 83248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood final int client_version = 3; 83348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 83448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_INIT (" + client_version + ")..."); 83548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 83648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(client_version); 83748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes()); 83848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 83948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Receive SSH_FXP_VERSION */ 84048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 84148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Waiting for SSH_FXP_VERSION..."); 84248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */ 84348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 84448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 84548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 84648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 84748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_VERSION) 84848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 84948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + t + ")"); 85048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 85148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 85248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood protocol_version = tr.readUINT32(); 85348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 85448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FXP_VERSION: protocol_version = " + protocol_version); 85548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (protocol_version != 3) 85648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 85748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Server version " + protocol_version + " is currently not supported"); 85848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 85948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 86048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Read and save extensions (if any) for later use */ 86148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 86248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (tr.remain() != 0) 86348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 86448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String name = tr.readString(); 86548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(name); 86648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] value = tr.readByteString(); 86748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length) + "'"); 86848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 86948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 87048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 87148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 87248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Returns the negotiated SFTP protocol version between the client and the server. 87348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 87448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return SFTP protocol version, i.e., "3". 87548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 87648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public int getProtocolVersion() 87748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 87848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return protocol_version; 87948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 88048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 88148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 88248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Queries the channel state 88348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return True if the underlying session is in open state 88448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 88548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public boolean isConnected() { 88648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return sess.getState() == Channel.STATE_OPEN; 88748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 88848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 88948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 89048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Close this SFTP session. NEVER forget to call this method to free up 89148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * resources - even if you got an exception from one of the other methods. 89248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Sometimes these other methods may throw an exception, saying that the 89348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * underlying channel is closed (this can happen, e.g., if the other server 89448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * sent a close message.) However, as long as you have not called the 89548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <code>close()</code> method, you are likely wasting resources. 89648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 89748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void close() 89848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 89948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sess.close(); 90048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 90148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 90248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 90348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * List the contents of a directory. 90448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 90548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param dirName See the {@link SFTPv3Client comment} for the class for more details. 90648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return A Vector containing {@link SFTPv3DirectoryEntry} objects. 90748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 90848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 90948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public List<SFTPv3DirectoryEntry> ls(String dirName) throws IOException 91048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 91148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood SFTPv3FileHandle handle = openDirectory(dirName); 91248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood List<SFTPv3DirectoryEntry> result = scanDirectory(handle.fileHandle); 91348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood closeFile(handle); 91448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return result; 91548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 91648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 91748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 91848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a new directory. 91948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 92048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param dirName See the {@link SFTPv3Client comment} for the class for more details. 92148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that 92248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * this is octal noation). The server will likely apply a umask. 92348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 92448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 92548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void mkdir(String dirName, int posixPermissions) throws IOException 92648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 92748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 92848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 92948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 93048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(dirName, charsetName); 93148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS); 93248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(posixPermissions); 93348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 93448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes()); 93548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 93648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 93748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 93848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 93948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 94048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Remove a file. 94148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 94248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 94348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 94448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 94548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void rm(String fileName) throws IOException 94648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 94748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 94848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 94948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 95048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(fileName, charsetName); 95148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 95248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes()); 95348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 95448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 95548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 95648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 95748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 95848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Remove an empty directory. 95948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 96048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param dirName See the {@link SFTPv3Client comment} for the class for more details. 96148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 96248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 96348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void rmdir(String dirName) throws IOException 96448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 96548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 96648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 96748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 96848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(dirName, charsetName); 96948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 97048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes()); 97148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 97248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 97348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 97448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 97548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 97648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Move a file or directory. 97748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 97848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param oldPath See the {@link SFTPv3Client comment} for the class for more details. 97948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param newPath See the {@link SFTPv3Client comment} for the class for more details. 98048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 98148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 98248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void mv(String oldPath, String newPath) throws IOException 98348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 98448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 98548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 98648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 98748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(oldPath, charsetName); 98848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(newPath, charsetName); 98948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 99048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes()); 99148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 99248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood expectStatusOKMessage(req_id); 99348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 99448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 99548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 99648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Open the file for reading. 99748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 99848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final int SSH_FXF_READ = 0x00000001; 99948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 100048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Open the file for writing. If both this and SSH_FXF_READ are 100148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * specified, the file is opened for both reading and writing. 100248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 100348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final int SSH_FXF_WRITE = 0x00000002; 100448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 100548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Force all writes to append data at the end of the file. 100648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 100748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final int SSH_FXF_APPEND = 0x00000004; 100848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 100948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * If this flag is specified, then a new file will be created if one 101048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * does not alread exist (if O_TRUNC is specified, the new file will 101148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * be truncated to zero length if it previously exists). 101248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 101348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final int SSH_FXF_CREAT = 0x00000008; 101448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 101548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Forces an existing file with the same name to be truncated to zero 101648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * length when creating a file by specifying SSH_FXF_CREAT. 101748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * SSH_FXF_CREAT MUST also be specified if this flag is used. 101848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 101948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final int SSH_FXF_TRUNC = 0x00000010; 102048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 102148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Causes the request to fail if the named file already exists. 102248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 102348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final int SSH_FXF_EXCL = 0x00000020; 102448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 102548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 102648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Open a file for reading. 102748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 102848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 102948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 103048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 103148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 103248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle openFileRO(String fileName) throws IOException 103348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 103448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return openFile(fileName, SSH_FXF_READ, null); 103548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 103648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 103748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 103848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Open a file for reading and writing. 103948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 104048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 104148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 104248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 104348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 104448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle openFileRW(String fileName) throws IOException 104548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 104648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return openFile(fileName, SSH_FXF_READ | SSH_FXF_WRITE, null); 104748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 104848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 104948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 105048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Open a file in append mode. The SFTP v3 draft says nothing but assuming normal POSIX 105148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * behavior, all writes will be appendend to the end of the file, no matter which offset 105248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * one specifies. 105348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 105448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * A side note for the curious: OpenSSH does an lseek() to the specified writing offset before each write(), 105548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * even for writes to files opened in O_APPEND mode. However, bear in mind that when working 105648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * in the O_APPEND mode, each write() includes an implicit lseek() to the end of the file 105748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (well, this is what the newsgroups say). 105848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 105948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 106048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 106148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 106248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 106348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException 106448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 106548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return openFile(fileName, SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND, null); 106648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 106748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 106848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 106948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Open a file in append mode. The SFTP v3 draft says nothing but assuming normal POSIX 107048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * behavior, all writes will be appendend to the end of the file, no matter which offset 107148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * one specifies. 107248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 107348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * A side note for the curious: OpenSSH does an lseek() to the specified writing offset before each write(), 107448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * even for writes to files opened in O_APPEND mode. However, bear in mind that when working 107548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * in the O_APPEND mode, each write() includes an implicit lseek() to the end of the file 107648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (well, this is what the newsgroups say). 107748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 107848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 107948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 108048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 108148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 108248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle openFileWAppend(String fileName) throws IOException 108348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 108448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return openFile(fileName, SSH_FXF_WRITE | SSH_FXF_APPEND, null); 108548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 108648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 108748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 108848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a file and open it for reading and writing. 108948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}. 109048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 109148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 109248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 109348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 109448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 109548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle createFile(String fileName) throws IOException 109648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 109748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return createFile(fileName, null); 109848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 109948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 110048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 110148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a file and open it for reading and writing. 110248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * You can specify the default attributes of the file (the server may or may 110348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * not respect your wishes). 110448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 110548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 110648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param attr may be <code>null</code> to use server defaults. Probably only 110748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the <code>uid</code>, <code>gid</code> and <code>permissions</code> 110848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} 110948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * structure make sense. You need only to set those fields where you want 111048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * to override the server's defaults. 111148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 111248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 111348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 111448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException 111548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 111648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return openFile(fileName, SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE, attr); 111748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 111848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 111948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 112048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Create a file (truncate it if it already exists) and open it for writing. 112148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}. 112248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 112348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 112448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 112548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 112648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 112748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException 112848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 112948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return createFileTruncate(fileName, null); 113048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 113148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 113248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 113348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * reate a file (truncate it if it already exists) and open it for writing. 113448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * You can specify the default attributes of the file (the server may or may 113548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * not respect your wishes). 113648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 113748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileName See the {@link SFTPv3Client comment} for the class for more details. 113848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param attr may be <code>null</code> to use server defaults. Probably only 113948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the <code>uid</code>, <code>gid</code> and <code>permissions</code> 114048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} 114148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * structure make sense. You need only to set those fields where you want 114248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * to override the server's defaults. 114348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return a SFTPv3FileHandle handle 114448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 114548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 114648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException 114748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 114848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return openFile(fileName, SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_WRITE, attr); 114948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 115048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 115148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private byte[] createAttrs(SFTPv3FileAttributes attr) 115248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 115348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 115448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 115548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int attrFlags = 0; 115648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 115748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (attr == null) 115848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 115948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(0); 116048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 116148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else 116248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 116348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (attr.size != null) 116448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 116548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE; 116648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 116748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 116848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((attr.uid != null) && (attr.gid != null)) 116948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 117048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID; 117148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 117248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 117348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (attr.permissions != null) 117448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 117548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS; 117648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 117748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 117848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((attr.atime != null) && (attr.mtime != null)) 117948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 118048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME; 118148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 118248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 118348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(attrFlags); 118448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 118548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (attr.size != null) 118648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 118748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT64(attr.size); 118848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 118948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 119048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((attr.uid != null) && (attr.gid != null)) 119148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 119248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(attr.uid); 119348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(attr.gid); 119448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 119548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 119648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (attr.permissions != null) 119748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 119848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(attr.permissions); 119948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 120048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 120148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((attr.atime != null) && (attr.mtime != null)) 120248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 120348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(attr.atime); 120448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(attr.mtime); 120548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 120648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 120748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 120848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return tw.getBytes(); 120948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 121048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 121148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException 121248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 121348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id = generateNextRequestID(); 121448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 121548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 121648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(fileName, charsetName); 121748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(flags); 121848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeBytes(createAttrs(attr)); 121948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 122048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_OPEN..."); 122148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes()); 122248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 122348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 122448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 122548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 122648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 122748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 122848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 122948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 123048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rep_id = tr.readUINT32(); 123148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (rep_id != req_id) 123248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 123348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 123448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 123548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 123648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_HANDLE) 123748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 123848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Got SSH_FXP_HANDLE."); 123948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return new SFTPv3FileHandle(this, tr.readByteString()); 124048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 124148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 124248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t != Packet.SSH_FXP_STATUS) 124348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 124448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 124548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 124648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 124748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int errorCode = tr.readUINT32(); 124848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String errorMessage = tr.readString(); 124948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(errorMessage); 125048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(errorMessage, errorCode); 125148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 125248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 125348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 125448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * A read is divided into multiple requests sent sequentially before 125548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * reading any status from the server 125648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 125748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static class OutstandingReadRequest 125848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 125948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id; 126048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 126148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Read offset to request on server starting at the file offset for the first request. 126248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 126348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood long serverOffset; 126448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 126548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Length of requested data 126648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 126748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int len; 126848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 126948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Offset in destination buffer 127048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 127148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int dstOffset; 127248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 127348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Temporary buffer 127448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 127548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] buffer; 127648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 127748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 127848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void sendReadRequest(int id, SFTPv3FileHandle handle, long offset, int len) throws IOException 127948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 128048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 128148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); 128248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT64(offset); 128348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT32(len); 128448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 128548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_READ (" + id + ") " + offset + "/" + len); 128648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_READ, id, tw.getBytes()); 128748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 128848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 128948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 129048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Parallel read requests maximum size. 129148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 129248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static final int DEFAULT_MAX_PARALLELISM = 64; 129348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 129448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 129548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Parallel read requests. 129648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 129748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private int parallelism = DEFAULT_MAX_PARALLELISM; 129848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 129948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 130048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param parallelism 130148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 130248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void setRequestParallelism(int parallelism) 130348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 130448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood this.parallelism = Math.min(parallelism, DEFAULT_MAX_PARALLELISM); 130548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 130648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 130748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 130848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Mapping request ID to request. 130948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 131048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood Map<Integer, OutstandingReadRequest> pendingReadQueue 131148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood = new HashMap<Integer, OutstandingReadRequest>(); 131248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 131348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 131448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Read bytes from a file in a parallel fashion. As many bytes as you want will be read. 131548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <p/> 131648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <ul> 131748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>The server will read as many bytes as it can from the file (up to <code>len</code>), 131848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * and return them.</li> 131948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>If EOF is encountered before reading any data, <code>-1</code> is returned. 132048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>If an error occurs, an exception is thrown</li>. 132148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * <li>For normal disk files, it is guaranteed that the server will return the specified 132248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * number of bytes, or up to end of file. For, e.g., device files this may return 132348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * fewer bytes than requested.</li> 132448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * </ul> 132548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 132648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param handle a SFTPv3FileHandle handle 132748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileOffset offset (in bytes) in the file 132848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param dst the destination byte array 132948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param dstoff offset in the destination byte array 133048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param len how many bytes to read, 0 < len 133148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @return the number of bytes that could be read, may be less than requested if 133248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the end of the file is reached, -1 is returned in case of <code>EOF</code> 133348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 133448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 133548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException 133648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 133748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood boolean errorOccured = false; 133848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 133948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood checkHandleValidAndOpen(handle); 134048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 134148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int remaining = len * parallelism; 134248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int clientOffset = dstoff; 134348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 134448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood long serverOffset = fileOffset; 134548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood for (OutstandingReadRequest r : pendingReadQueue.values()) 134648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 134748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Server offset should take pending requests into account. 134848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood serverOffset += r.len; 134948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 135048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 135148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (true) 135248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 135348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Stop if there was an error and no outstanding request 135448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((pendingReadQueue.size() == 0) && errorOccured) 135548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 135648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 135748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 135848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 135948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Send as many requests as we are allowed to 136048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (pendingReadQueue.size() < parallelism) 136148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 136248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (errorOccured) 136348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 136448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 136548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 136648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Send the next read request 136748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood OutstandingReadRequest req = new OutstandingReadRequest(); 136848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.req_id = generateNextRequestID(); 136948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.serverOffset = serverOffset; 137048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.len = (remaining > len) ? len : remaining; 137148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.buffer = dst; 137248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.dstOffset = dstoff; 137348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 137448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood serverOffset += req.len; 137548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood clientOffset += req.len; 137648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood remaining -= req.len; 137748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 137848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendReadRequest(req.req_id, handle, req.serverOffset, req.len); 137948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 138048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood pendingReadQueue.put(req.req_id, req); 138148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 138248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (pendingReadQueue.size() == 0) 138348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 138448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 138548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 138648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 138748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Receive a single answer 138848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 138948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 139048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 139148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 139248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 139348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 139448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Search the pending queue 139548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood OutstandingReadRequest req = pendingReadQueue.remove(tr.readUINT32()); 139648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (null == req) 139748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 139848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 139948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 140048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Evaluate the answer 140148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_STATUS) 140248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 140348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* In any case, stop sending more packets */ 140448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 140548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int code = tr.readUINT32(); 140648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String msg = tr.readString(); 140748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(msg); 140848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 140948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (log.isDebugEnabled()) 141048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 141148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String[] desc = ErrorCodes.getDescription(code); 141248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Got SSH_FXP_STATUS (" + req.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")"); 141348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 141448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Flag to read all pending requests but don't send any more. 141548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood errorOccured = true; 141648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (pendingReadQueue.isEmpty()) 141748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 141848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ErrorCodes.SSH_FX_EOF == code) 141948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 142048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return -1; 142148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 142248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(msg, code); 142348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 142448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 142548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else if (t == Packet.SSH_FXP_DATA) 142648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 142748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // OK, collect data 142848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int readLen = tr.readUINT32(); 142948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 143048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((readLen < 0) || (readLen > req.len)) 143148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 143248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid length field in a SSH_FXP_DATA packet."); 143348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 143448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 143548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (log.isDebugEnabled()) 143648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 143748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Got SSH_FXP_DATA (" + req.req_id + ") " + req.serverOffset + "/" + readLen 143848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood + " (requested: " + req.len + ")"); 143948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 144048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 144148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Read bytes into buffer 144248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tr.readBytes(req.buffer, req.dstOffset, readLen); 144348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 144448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (readLen < req.len) 144548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 144648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Send this request packet again to request the remaing data in this slot. */ 144748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.req_id = generateNextRequestID(); 144848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.serverOffset += readLen; 144948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.len -= readLen; 145048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 145148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Requesting again: " + req.serverOffset + "/" + req.len); 145248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendReadRequest(req.req_id, handle, req.serverOffset, req.len); 145348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 145448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood pendingReadQueue.put(req.req_id, req); 145548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 145648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return readLen; 145748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 145848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else 145948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 146048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 146148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 146248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 146348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Should never reach here. 146448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException("No EOF reached", -1); 146548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 146648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 146748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 146848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * A read is divided into multiple requests sent sequentially before 146948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * reading any status from the server 147048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 147148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static class OutstandingStatusRequest 147248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 147348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int req_id; 147448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 147548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 147648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 147748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Mapping request ID to request. 147848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 147948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood Map<Integer, OutstandingStatusRequest> pendingStatusQueue 148048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood = new HashMap<Integer, OutstandingStatusRequest>(); 148148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 148248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 148348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Write bytes to a file. If <code>len</code> > 32768, then the write operation will 148448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * be split into multiple writes. 148548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 148648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param handle a SFTPv3FileHandle handle. 148748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param fileOffset offset (in bytes) in the file. 148848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param src the source byte array. 148948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param srcoff offset in the source byte array. 149048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param len how many bytes to write. 149148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 149248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 149348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException 149448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 149548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood checkHandleValidAndOpen(handle); 149648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 149748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Send the next write request 149848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood OutstandingStatusRequest req = new OutstandingStatusRequest(); 149948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood req.req_id = generateNextRequestID(); 150048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 150148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesWriter tw = new TypesWriter(); 150248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); 150348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeUINT64(fileOffset); 150448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood tw.writeString(src, srcoff, len); 150548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 150648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Sending SSH_FXP_WRITE..."); 150748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood sendMessage(Packet.SSH_FXP_WRITE, req.req_id, tw.getBytes()); 150848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 150948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood pendingStatusQueue.put(req.req_id, req); 151048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 151148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Only read next status if parallelism reached 151248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (pendingStatusQueue.size() >= parallelism) 151348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 151448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood this.readStatus(); 151548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 151648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 151748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 151848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void readStatus() throws IOException 151948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 152048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 152148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 152248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 152348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 152448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 152548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 152648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Search the pending queue 152748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood OutstandingStatusRequest status = pendingStatusQueue.remove(tr.readUINT32()); 152848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (null == status) 152948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 153048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 153148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 153248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 153348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Evaluate the answer 153448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_STATUS) 153548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 153648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // In any case, stop sending more packets 153748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int code = tr.readUINT32(); 153848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (log.isDebugEnabled()) 153948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 154048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String[] desc = ErrorCodes.getDescription(code); 154148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")"); 154248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 154348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (code == ErrorCodes.SSH_FX_OK) 154448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 154548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return; 154648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 154748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String msg = tr.readString(); 154848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(msg); 154948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(msg, code); 155048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 155148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 155248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 155348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 155448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private void readPendingReadStatus() throws IOException 155548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 155648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] resp = receiveMessage(34000); 155748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 155848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood TypesReader tr = new TypesReader(resp); 155948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int t = tr.readByte(); 156048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(Packet.forName(t)); 156148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 156248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Search the pending queue 156348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood OutstandingReadRequest status = pendingReadQueue.remove(tr.readUINT32()); 156448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (null == status) 156548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 156648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The server sent an invalid id field."); 156748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 156848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 156948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Evaluate the answer 157048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (t == Packet.SSH_FXP_STATUS) 157148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 157248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // In any case, stop sending more packets 157348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int code = tr.readUINT32(); 157448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (log.isDebugEnabled()) 157548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 157648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String[] desc = ErrorCodes.getDescription(code); 157748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")"); 157848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 157948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (code == ErrorCodes.SSH_FX_OK) 158048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 158148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return; 158248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 158348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (code == ErrorCodes.SSH_FX_EOF) 158448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 158548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return; 158648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 158748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String msg = tr.readString(); 158848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood listener.read(msg); 158948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new SFTPException(msg, code); 159048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 159148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); 159248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 159348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 159448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /** 159548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Close a file. 159648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 159748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @param handle a SFTPv3FileHandle handle 159848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @throws IOException 159948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 160048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public void closeFile(SFTPv3FileHandle handle) throws IOException 160148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 160248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood try 160348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 160448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (!pendingReadQueue.isEmpty()) 160548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 160648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood this.readPendingReadStatus(); 160748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 160848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (!pendingStatusQueue.isEmpty()) 160948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 161048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood this.readStatus(); 161148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 161248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (!handle.isClosed) 161348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 161448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood closeHandle(handle.fileHandle); 161548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 161648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 161748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood finally 161848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 161948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood handle.isClosed = true; 162048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 162148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 162248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood} 1623