You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2013/01/17 14:09:52 UTC

svn commit: r1434658 [5/6] - in /mina/sshd/trunk: sshd-core/src/docs/ sshd-core/src/main/java/org/apache/sshd/server/filesystem/ sshd-sftp/ sshd-sftp/src/main/java/org/apache/sshd/sftp/ sshd-sftp/src/main/java/org/apache/sshd/sftp/reply/ sshd-sftp/src/...

Modified: mina/sshd/trunk/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java?rev=1434658&r1=1434657&r2=1434658&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java (original)
+++ mina/sshd/trunk/sshd-sftp/src/main/java/org/apache/sshd/sftp/subsystem/SftpSubsystem.java Thu Jan 17 13:09:51 2013
@@ -19,14 +19,14 @@
 package org.apache.sshd.sftp.subsystem;
 
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Session;
 import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.SelectorUtils;
 import org.apache.sshd.server.*;
+import org.apache.sshd.server.channel.ChannelDataReceiver;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.sftp.DefaultSftpletContainer;
-import org.apache.sshd.sftp.Handle;
-import org.apache.sshd.sftp.Sftplet;
+import org.apache.sshd.sftp.*;
 import org.apache.sshd.sftp.reply.*;
 import org.apache.sshd.sftp.request.*;
 import org.slf4j.Logger;
@@ -35,15 +35,16 @@ import org.slf4j.LoggerFactory;
 import java.io.*;
 import java.util.*;
 
+import static org.apache.sshd.sftp.subsystem.SftpConstants.*;
+
 /**
  * SFTP subsystem
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class SftpSubsystem implements Command, Runnable, SessionAware, FileSystemAware {
-    private Sftplet sftpLet = new DefaultSftpletContainer();
+public class SftpSubsystem implements Command, SessionAware, FileSystemAware, SftpSession, ChannelDataReceiver, ChannelSessionAware {
 
-    protected final Logger log = LoggerFactory.getLogger(getClass());
+    protected static final Logger LOG = LoggerFactory.getLogger(SftpSubsystem.class);
 
     public static class Factory implements NamedFactory<Command> {
 
@@ -59,168 +60,16 @@ public class SftpSubsystem implements Co
         }
     }
 
+    public static final int LOWER_SFTP_IMPL = 3; // Working implementation from v3
+    public static final int HIGHER_SFTP_IMPL = 6; //  .. up to
+    public static final String ALL_SFTP_IMPL = "3,4,5,6";
+    public static final int  MAX_PACKET_LENGTH = 1024 * 16;
+
     /**
      * Properties key for the maximum of available open handles per session.
      */
     public static final String MAX_OPEN_HANDLES_PER_SESSION = "max-open-handles-per-session";
 
-    public static final int LOWER_SFTP_IMPL = 3; // Working implementation from v3
-    public static final int HIGHER_SFTP_IMPL = 3; //  .. up to
-    public static final String ALL_SFTP_IMPL = "3";
-    public static final int  MAX_PACKET_LENGTH = 1024 * 16;
-
-    public static final int SSH_FXP_INIT = 1;
-    public static final int SSH_FXP_VERSION = 2;
-    public static final int SSH_FXP_OPEN = 3;
-    public static final int SSH_FXP_CLOSE = 4;
-    public static final int SSH_FXP_READ = 5;
-    public static final int SSH_FXP_WRITE = 6;
-    public static final int SSH_FXP_LSTAT = 7;
-    public static final int SSH_FXP_FSTAT = 8;
-    public static final int SSH_FXP_SETSTAT = 9;
-    public static final int SSH_FXP_FSETSTAT = 10;
-    public static final int SSH_FXP_OPENDIR = 11;
-    public static final int SSH_FXP_READDIR = 12;
-    public static final int SSH_FXP_REMOVE = 13;
-    public static final int SSH_FXP_MKDIR = 14;
-    public static final int SSH_FXP_RMDIR = 15;
-    public static final int SSH_FXP_REALPATH = 16;
-    public static final int SSH_FXP_STAT = 17;
-    public static final int SSH_FXP_RENAME = 18;
-    public static final int SSH_FXP_READLINK = 19;
-    public static final int SSH_FXP_LINK = 21;
-    public static final int SSH_FXP_BLOCK = 22;
-    public static final int SSH_FXP_UNBLOCK = 23;
-
-    public static final int SSH_FXP_STATUS = 101;
-    public static final int SSH_FXP_HANDLE = 102;
-    public static final int SSH_FXP_DATA = 103;
-    public static final int SSH_FXP_NAME = 104;
-    public static final int SSH_FXP_ATTRS = 105;
-
-    public static final int SSH_FXP_EXTENDED = 200;
-    public static final int SSH_FXP_EXTENDED_REPLY = 201;
-
-    public static final int SSH_FX_OK = 0;
-    public static final int SSH_FX_EOF = 1;
-    public static final int SSH_FX_NO_SUCH_FILE = 2;
-    public static final int SSH_FX_PERMISSION_DENIED = 3;
-    public static final int SSH_FX_FAILURE = 4;
-    public static final int SSH_FX_BAD_MESSAGE = 5;
-    public static final int SSH_FX_NO_CONNECTION = 6;
-    public static final int SSH_FX_CONNECTION_LOST = 7;
-    public static final int SSH_FX_OP_UNSUPPORTED = 8;
-    public static final int SSH_FX_INVALID_HANDLE = 9;
-    public static final int SSH_FX_NO_SUCH_PATH = 10;
-    public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
-    public static final int SSH_FX_WRITE_PROTECT = 12;
-    public static final int SSH_FX_NO_MEDIA = 13;
-    public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
-    public static final int SSH_FX_QUOTA_EXCEEDED = 15;
-    public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
-    public static final int SSH_FX_LOCK_CONFLICT = 17;
-    public static final int SSH_FX_DIR_NOT_EMPTY = 18;
-    public static final int SSH_FX_NOT_A_DIRECTORY = 19;
-    public static final int SSH_FX_INVALID_FILENAME = 20;
-    public static final int SSH_FX_LINK_LOOP = 21;
-    public static final int SSH_FX_CANNOT_DELETE = 22;
-    public static final int SSH_FX_INVALID_PARAMETER = 23;
-    public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
-    public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
-    public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
-    public static final int SSH_FX_DELETE_PENDING = 27;
-    public static final int SSH_FX_FILE_CORRUPT = 28;
-    public static final int SSH_FX_OWNER_INVALID = 29;
-    public static final int SSH_FX_GROUP_INVALID = 30;
-    public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
-
-    public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
-    public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
-    public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; //v3 naming convention
-    public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
-    public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
-    public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
-    public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
-    public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
-    public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
-    public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
-    public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
-    public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
-    public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
-    public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
-    public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
-    public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
-    public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
-
-    public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
-    public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
-    public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
-    public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
-    public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
-    public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
-    public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
-    public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
-    public static final int SSH_FILEXFER_TYPE_FIFO = 9;
-
-
-    public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
-    public static final int SSH_FXF_CREATE_NEW = 0x00000000;
-    public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
-    public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
-    public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
-    public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
-    public static final int SSH_FXF_APPEND_DATA = 0x00000008;
-    public static final int SSH_FXF_APPEND_DATA_ATOMIC = 0x00000010;
-    public static final int SSH_FXF_TEXT_MODE = 0x00000020;
-    public static final int SSH_FXF_BLOCK_READ = 0x00000040;
-    public static final int SSH_FXF_BLOCK_WRITE = 0x00000080;
-    public static final int SSH_FXF_BLOCK_DELETE = 0x00000100;
-    public static final int SSH_FXF_BLOCK_ADVISORY = 0x00000200;
-    public static final int SSH_FXF_NOFOLLOW = 0x00000400;
-    public static final int SSH_FXF_DELETE_ON_CLOSE = 0x00000800;
-    public static final int SSH_FXF_ACCESS_AUDIT_ALARM_INFO = 0x00001000;
-    public static final int SSH_FXF_ACCESS_BACKUP = 0x00002000;
-    public static final int SSH_FXF_BACKUP_STREAM = 0x00004000;
-    public static final int SSH_FXF_OVERRIDE_OWNER = 0x00008000;
-
-    public static final int SSH_FXF_READ = 0x00000001;
-    public static final int SSH_FXF_WRITE = 0x00000002;
-    public static final int SSH_FXF_APPEND = 0x00000004;
-    public static final int SSH_FXF_CREAT = 0x00000008;
-    public static final int SSH_FXF_TRUNC = 0x00000010;
-    public static final int SSH_FXF_EXCL = 0x00000020;
-    public static final int SSH_FXF_TEXT = 0x00000040;
-
-    public static final int ACE4_READ_DATA = 0x00000001;
-    public static final int ACE4_LIST_DIRECTORY = 0x00000001;
-    public static final int ACE4_WRITE_DATA = 0x00000002;
-    public static final int ACE4_ADD_FILE = 0x00000002;
-    public static final int ACE4_APPEND_DATA = 0x00000004;
-    public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
-    public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
-    public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
-    public static final int ACE4_EXECUTE = 0x00000020;
-    public static final int ACE4_DELETE_CHILD = 0x00000040;
-    public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
-    public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
-    public static final int ACE4_DELETE = 0x00010000;
-    public static final int ACE4_READ_ACL = 0x00020000;
-    public static final int ACE4_WRITE_ACL = 0x00040000;
-    public static final int ACE4_WRITE_OWNER = 0x00080000;
-
-    public static final int S_IRUSR = 0000400;
-    public static final int S_IWUSR = 0000200;
-    public static final int S_IXUSR = 0000100;
-    public static final int S_IRGRP = 0000040;
-    public static final int S_IWGRP = 0000020;
-    public static final int S_IXGRP = 0000010;
-    public static final int S_IROTH = 0000004;
-    public static final int S_IWOTH = 0000002;
-    public static final int S_IXOTH = 0000001;
-    public static final int S_ISUID = 0004000;
-    public static final int S_ISGID = 0002000;
-    public static final int S_ISVTX = 0001000;
-
 
     private ExitCallback callback;
     private InputStream in;
@@ -228,199 +77,57 @@ public class SftpSubsystem implements Co
     private OutputStream err;
     private Environment env;
     private ServerSession session;
+    private ChannelSession channel;
     private boolean closed = false;
 
     private FileSystemView root;
 
     private int version;
     private Map<String, Handle> handles = new HashMap<String, Handle>();
-	private Request sftpRequest;
 
-    
-    protected static int mapV4ToV3(int code) {
-        switch (code) {
-            case SSH_FX_INVALID_HANDLE:
-                return SSH_FX_FAILURE;
-            case SSH_FX_NO_SUCH_PATH:
-                return SSH_FX_NO_SUCH_FILE;
-            case SSH_FX_FILE_ALREADY_EXISTS:
-                return SSH_FX_FAILURE;
-            case SSH_FX_WRITE_PROTECT:
-                return SSH_FX_PERMISSION_DENIED;
-            case SSH_FX_NO_MEDIA:
-                return SSH_FX_FAILURE;
-            default:
-                return code;
-        }
-    }
-    
-    protected static int mapV5ToV4(int code) {
-        switch (code) {
-            case SSH_FX_NO_SPACE_ON_FILESYSTEM:
-                return SSH_FX_FAILURE;
-            case SSH_FX_QUOTA_EXCEEDED:
-                return SSH_FX_FAILURE;
-            case SSH_FX_UNKNOWN_PRINCIPAL:
-                return SSH_FX_FAILURE;
-            case SSH_FX_LOCK_CONFLICT:
-                return SSH_FX_FAILURE;
-            default:
-                return code;
-        }
-    }
-
-    protected static int mapV6ToV5(int code) {
-        switch (code) {
-            case SSH_FX_DIR_NOT_EMPTY:
-                return SSH_FX_FAILURE;
-            case SSH_FX_NOT_A_DIRECTORY:
-                return SSH_FX_NO_SUCH_FILE;
-            case SSH_FX_INVALID_FILENAME:
-                return SSH_FX_NO_SUCH_FILE;
-            case SSH_FX_LINK_LOOP:
-                return SSH_FX_FAILURE;
-            case SSH_FX_CANNOT_DELETE:
-                return SSH_FX_PERMISSION_DENIED;
-            case SSH_FX_INVALID_PARAMETER:
-                return SSH_FX_FAILURE;
-            case SSH_FX_FILE_IS_A_DIRECTORY:
-                return SSH_FX_NO_SUCH_FILE;
-            case SSH_FX_BYTE_RANGE_LOCK_CONFLICT:
-                return SSH_FX_FAILURE;
-            case SSH_FX_BYTE_RANGE_LOCK_REFUSED:
-                return SSH_FX_FAILURE;
-            case SSH_FX_DELETE_PENDING:
-                return SSH_FX_FAILURE;
-            case SSH_FX_FILE_CORRUPT:
-                return SSH_FX_FAILURE;
-            case SSH_FX_OWNER_INVALID:
-                return SSH_FX_PERMISSION_DENIED;
-            case SSH_FX_GROUP_INVALID:
-                return SSH_FX_PERMISSION_DENIED;
-            case SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK:
-                return SSH_FX_FAILURE;
-            default:
-                return code;
-        }
-    }
-    
-    protected static int mapToVersion(int code, int version) {
-        int mappedCode = code;
-        if (version < 6) {
-            mappedCode = mapV6ToV5(mappedCode);
-        }
-        if (version < 5) {
-            mappedCode = mapV5ToV4(mappedCode);
-        }
-        if (version < 4) {
-            mappedCode = mapV4ToV3(mappedCode);
-        }
-        return mappedCode;
-    }
-    
-    protected int mapToVersion(int code) {
-        return mapToVersion(code, version);
-    }
-
-    protected static class DirectoryHandle extends Handle implements Iterator<SshFile> {
-        boolean done;
-        // the directory should be read once at "open directory"
-        List<SshFile> fileList = null;
-        int fileIndex;
-
-        public DirectoryHandle(SshFile file) {
-            super(file);
-            fileList = file.listSshFiles();
-            fileIndex = 0;
-        }
-
-        public boolean isDone() {
-            return done;
-        }
-
-        public void setDone(boolean done) {
-            this.done = done;
-        }
-
-        public boolean hasNext() {
-            return fileIndex < fileList.size();
-        }
-
-        public SshFile next() {
-            SshFile f = fileList.get(fileIndex);
-            fileIndex++;
-            return f;
-        }
-
-        public void remove() {
-            throw new UnsupportedOperationException();
-        }
-
-        public void clearFileList() {
-            // allow the garbage collector to do the job
-            fileList = null;
-        }
-    }
-
-    public static class FileHandle extends Handle {
-        int flags;
-        OutputStream output;
-        long outputPos;
-        InputStream input;
-        long inputPos;
-
-        public FileHandle(SshFile sshFile, int flags) {
-            super(sshFile);
-            this.flags = flags;
-        }
-
-        public int getFlags() {
-            return flags;
-        }
-        
-        public int read(byte[] data, long offset) throws IOException {
-            if (input != null && offset != inputPos) {
-                IoUtils.closeQuietly(input);
-                input = null;
-            }
-            if (input == null) {
-                input = file.createInputStream(offset);
-                inputPos = offset;
-            }
-            int read = input.read(data);
-            inputPos += read;
-            return read;
-        }
+    private Sftplet sftpLet = new DefaultSftpletContainer();
+    private Serializer serializer = new Serializer(this);
 
-        public void write(byte[] data, long offset) throws IOException {
-            if (output != null && offset != outputPos) {
-                IoUtils.closeQuietly(output);
-                output = null;
-            }
-            if (output == null) {
-                output = file.createOutputStream(offset);
-            }
-            output.write(data);
-            outputPos += data.length;
-        }
+    public SftpSubsystem() {
+    }
 
-        @Override
-        public void close() throws IOException {
-            IoUtils.closeQuietly(output, input);
-            output = null;
-            input = null;
-            super.close();
-        }
+    public void setSftpLet(final Sftplet sftpLet) {
+        this.sftpLet = sftpLet;
     }
 
-    public SftpSubsystem() {}
+    public int getVersion() {
+        return version;
+    }
 
-    public void setSftpLet(final Sftplet sftpLet) {
-		this.sftpLet = sftpLet;
+    public Session getSession() {
+        return session;
+    }
+
+    public Handle getHandle(String id) {
+        return handles.get(id);
     }
-    
+
+    public Handle createFileHandle(SshFile file, int flags) {
+        String id = UUID.randomUUID().toString();
+        Handle handle = new FileHandle(id,  file, flags);
+        handles.put(id, handle);
+        return handle;
+    }
+
+    public Handle createDirectoryHandle(SshFile file) {
+        String id = UUID.randomUUID().toString();
+        Handle handle = new DirectoryHandle(id, file);
+        handles.put(id, handle);
+        return handle;
+    }
+
+    public void setChannelSession(ChannelSession channel) {
+        this.channel = channel;
+        channel.setDataReceiver(this);
+    }
+
     public void setSession(ServerSession session) {
-        sftpLet.onConnect(session);
+        sftpLet.onConnect(this);
         this.session = session;
     }
 
@@ -446,558 +153,513 @@ public class SftpSubsystem implements Co
 
     public void start(Environment env) throws IOException {
         this.env = env;
-        new Thread(this).start();
     }
 
-    public void run() {
-        DataInputStream dis = null;
-        try {
-            dis = new DataInputStream(in);
-            while (true) {
-                int length = dis.readInt();
-                if (length < 5) {
-                    throw new IllegalArgumentException();
-                }
-                Buffer buffer = new Buffer(length + 4);
-                buffer.putInt(length);
-                int nb = length;
-                while (nb > 0) {
-                    int l = dis.read(buffer.array(), buffer.wpos(), nb);
-                    if (l < 0) {
-                        throw new IllegalArgumentException();
-                    }
-                    buffer.wpos(buffer.wpos() + l);
-                    nb -= l;
-                }
-                process(buffer);
-            }
-        } catch (Throwable t) {
-            if (!closed && !(t instanceof EOFException)) { // Ignore han
-                log.error("Exception caught in SFTP subsystem", t);
-            }
-        } finally {
-            if (dis != null) {
+    private Buffer buffer = new Buffer();
+
+    public int data(ChannelSession channel, byte[] buf, int start, int len) throws IOException {
+        Buffer incoming = new Buffer(buf,  start, len);
+        // If we already have partial data, we need to append it to the buffer and use it
+        if (buffer.available() > 0) {
+            buffer.putBuffer(incoming);
+            incoming = buffer;
+        }
+        // Process commands
+        int rpos = incoming.rpos();
+        while (receive(incoming));
+        int read = incoming.rpos() - rpos;
+        // Compact and add remaining data
+        buffer.compact();
+        if (buffer != incoming && incoming.available() > 0) {
+            buffer.putBuffer(incoming);
+        }
+        return read;
+    }
+
+    protected boolean receive(Buffer incoming) throws IOException {
+        int rpos = incoming.rpos();
+        int wpos = incoming.wpos();
+        if (wpos - rpos > 4) {
+            int length = incoming.getInt();
+            if (length < 5) {
+                throw new IOException("Illegal sftp packet length: " + length);
+            }
+            if (wpos - rpos >= length + 4) {
+                incoming.rpos(rpos);
+                incoming.wpos(rpos + 4 + length);
+                process(incoming);
+                incoming.rpos(rpos + 4 + length);
+                incoming.wpos(wpos);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void close() throws IOException {
+        if (handles != null) {
+            for (Map.Entry<String, Handle> entry : handles.entrySet()) {
+                Handle handle = entry.getValue();
                 try {
-                    dis.close();
+                    handle.close();
                 } catch (IOException ioe) {
-                    log.error("Could not close DataInputStream", ioe);
+                    LOG.error("Could not close open handle: " + entry.getKey(), ioe);
                 }
             }
+        }
+        callback.onExit(0);
+        sftpLet.onDisconnect(this);
+    }
 
-            if (handles != null) {
-                for (Map.Entry<String, Handle> entry : handles.entrySet()) {
-                    Handle handle = entry.getValue();
-                    try {
-                        handle.close();
-                    } catch (IOException ioe) {
-                        log.error("Could not close open handle: " + entry.getKey(), ioe);
-                    }
-                }
+    public void process(Buffer buffer) throws IOException {
+        Request request = serializer.readRequest(buffer);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Received sftp request: " + request);
+        }
+        Reply reply = sftpLet.beforeCommand(this, request);
+        if (reply == null) {
+            reply = doProcess(request);
+        }
+        reply = sftpLet.afterCommand(this, request, reply);
+        if (reply != null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Sending sftp reply: " + reply);
             }
-            dis = null;
+            buffer = serializer.writeReply(reply);
+            send(buffer);
+        }
+    }
+
+    protected Reply doProcess(Request request) throws IOException {
+        try {
+            if (request instanceof SshFxpInitRequest) {
+                return doProcessInit((SshFxpInitRequest) request);
+            } else if (request instanceof SshFxpOpenRequest) {
+                return doProcessOpen((SshFxpOpenRequest) request);
+            } else if (request instanceof SshFxpCloseRequest) {
+                return doProcessClose((SshFxpCloseRequest) request);
+            } else if (request instanceof SshFxpReadRequest) {
+                return doProcessRead((SshFxpReadRequest) request);
+            } else if (request instanceof SshFxpWriteRequest) {
+                return doProcessWrite((SshFxpWriteRequest) request);
+            } else if ((request instanceof SshFxpLstatRequest)
+                    || (request instanceof SshFxpStatRequest)) {
+                return doProcessStat(request);
+            } else if (request instanceof SshFxpFstatRequest) {
+                return doProcessFstat((SshFxpFstatRequest) request);
+            } else if (request instanceof SshFxpOpendirRequest) {
+                return doProcessOpendir((SshFxpOpendirRequest) request);
+            } else if (request instanceof SshFxpReaddirRequest) {
+                return doProcessReaddir((SshFxpReaddirRequest) request);
+            } else if (request instanceof SshFxpRemoveRequest) {
+                return doProcessRemove((SshFxpRemoveRequest) request);
+            } else if (request instanceof SshFxpMkdirRequest) {
+                return doProcessMkdir((SshFxpMkdirRequest) request);
+            } else if (request instanceof SshFxpRmdirRequest) {
+                return doProcessRmdir((SshFxpRmdirRequest) request);
+            } else if (request instanceof SshFxpRealpathRequest) {
+                return doProcessRealpath((SshFxpRealpathRequest) request);
+            } else if (request instanceof SshFxpRenameRequest) {
+                return doProcessRename((SshFxpRenameRequest) request);
+            } else if ((request instanceof SshFxpSetstatRequest)
+                    || (request instanceof SshFxpFsetstatRequest)) {
+                return doProcessSetstat(request);
 
-            callback.onExit(0);
-            sftpLet.onDisconnect(session);
+            } else {
+                LOG.error("Received: {}", request);
+                int id = request.getId();
+                return new SshFxpStatusReply(id, SSH_FX_OP_UNSUPPORTED, "Command " + request + " is unsupported or not implemented");
+            }
+        } catch (IOException e) {
+            int id = request.getId();
+            return new SshFxpStatusReply(id, SSH_FX_FAILURE, e.getMessage());
         }
     }
 
-    public void process(Buffer buffer) throws IOException {
-    	sftpRequest = getSftpRequest(new Buffer(buffer.array()));
-    	Reply reply = sftpLet.beforeCommand(session, sftpRequest);
-    	if (reply != null) {
-    		sendReply(reply);
-    		return;
-    	}
-        int id = sftpRequest.getId();
-        if (sftpRequest instanceof SshFxpInitRequest) {
-                version = id;
-                if (version >= LOWER_SFTP_IMPL) {
-                    version = Math.min(version, HIGHER_SFTP_IMPL);
-                    Buffer sendBuffer = new Buffer();
-                    sendBuffer.putByte((byte) SSH_FXP_VERSION);
-                    sendBuffer.putInt(version);
-                    send(sendBuffer);
-                } else {
-                    // We only support version 3 (Version 1 and 2 are not common)
-                    sendStatus(id, SSH_FX_OP_UNSUPPORTED, "SFTP server only support versions " + ALL_SFTP_IMPL);
-                }
-        } else if (sftpRequest instanceof SshFxpOpenRequest) {
-        	SshFxpOpenRequest sshFxpOpenRequest = (SshFxpOpenRequest) sftpRequest;
-                if (session.getFactoryManager().getProperties() != null) {
-                    String maxHandlesString = session.getFactoryManager().getProperties().get(MAX_OPEN_HANDLES_PER_SESSION);
-                    if (maxHandlesString != null) {
-                        int maxHandleCount = Integer.parseInt(maxHandlesString);
-                        if (handles.size() > maxHandleCount) {
-                            sendStatus(id, SSH_FX_FAILURE, "Too many open handles");
-                            return;
-                        }
-                    }
-                }
+    private Reply doProcessSetstat(Request request) throws IOException {
+        // This is required for WinSCP / Cyberduck to upload properly
+        // Blindly reply "OK"
+        // TODO implement it
+        int id = request.getId();
+        return new SshFxpStatusReply(id, SSH_FX_OK, "");
+    }
+
+    private Reply doProcessRename(SshFxpRenameRequest request) throws IOException {
+        int id = request.getId();
+        String oldPath = request.getOldPath();
+        String newPath = request.getNewPath();
+        SshFile o = resolveFile(oldPath);
+        SshFile n = resolveFile(newPath);
+        if (!o.doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, o.getAbsolutePath());
+        } else if (n.doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, n.getAbsolutePath());
+        } else if (!o.move(n)) {
+            return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Failed to rename file");
+        } else {
+            return new SshFxpStatusReply(id, SSH_FX_OK, "");
+        }
+    }
 
-                Integer accValue = sshFxpOpenRequest.getAcc();
-                if (accValue == null) {
-                    String path = sshFxpOpenRequest.getPath();
-                    int pflags = sshFxpOpenRequest.getFlags();
-                    // attrs
-                    try {
-                        SshFile file = resolveFile(path);
-                        if (file.doesExist()) {
-                            if (((pflags & SSH_FXF_CREAT) != 0) && ((pflags & SSH_FXF_EXCL) != 0)) {
-                                sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, path);
-                                return;
-                            }
-                        } else {
-                            if (((pflags & SSH_FXF_CREAT) != 0)) {
-                                if (!file.isWritable()) {
-                                    sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
-                                    return;
-                                }
-                                file.create();
-                            }
-                        }
-                        String acc = ((pflags & (SSH_FXF_READ | SSH_FXF_WRITE)) != 0 ? "r" : "") +
-                                ((pflags & SSH_FXF_WRITE) != 0 ? "w" : "");
-                        if ((pflags & SSH_FXF_TRUNC) != 0) {
-                            file.truncate();
-                        }
-                        String handle = UUID.randomUUID().toString();
-                        handles.put(handle, new FileHandle(file, pflags)); // handle flags conversion
-                        sendHandle(id, handle);
-                    } catch (IOException e) {
-                        sendStatus(id, SSH_FX_FAILURE, e.getMessage() == null ? "" : e.getMessage());
-                    }
-                } else {
-                    String path = sshFxpOpenRequest.getPath();
-                    int acc = accValue;
-                    int flags = sshFxpOpenRequest.getFlags();
-                    // attrs
-                    try {
-                        SshFile file = resolveFile(path);
-                        switch (flags & SSH_FXF_ACCESS_DISPOSITION) {
-                            case SSH_FXF_CREATE_NEW: {
-                                if (file.doesExist()) {
-                                    sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, path);
-                                    return;
-                                } else if (!file.isWritable()) {
-                                    sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
-                                }
-                                file.create();
-                                break;
-                            }
-                            case SSH_FXF_CREATE_TRUNCATE: {
-                                if (file.doesExist()) {
-                                    sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, path);
-                                    return;
-                                } else if (!file.isWritable()) {
-                                    sendStatus(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
-                                }
-                                file.truncate();
-                                break;
-                            }
-                            case SSH_FXF_OPEN_EXISTING: {
-                                if (!file.doesExist()) {
-                                    if (!file.getParentFile().doesExist()) {
-                                        sendStatus(id, SSH_FX_NO_SUCH_PATH, path);
-                                    } else {
-                                        sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
-                                    }
-                                    return;
-                                }
-                                break;
-                            }
-                            case SSH_FXF_OPEN_OR_CREATE: {
-                                if (!file.doesExist()) {
-                                    file.create();
-                                }
-                                break;
-                            }
-                            case SSH_FXF_TRUNCATE_EXISTING: {
-                                if (!file.doesExist()) {
-                                    if (!file.getParentFile().doesExist()) {
-                                        sendStatus(id, SSH_FX_NO_SUCH_PATH, path);
-                                    } else {
-                                        sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
-                                    }
-                                    return;
-                                }
-                                file.truncate();
-                                break;
-                            }
-                            default:
-                                throw new IllegalArgumentException("Unsupported open mode: " + flags);
-                        }
-                        String handle = UUID.randomUUID().toString();
-                        handles.put(handle, new FileHandle(file, flags));
-                        sendHandle(id, handle);
-                    } catch (IOException e) {
-                        sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                    }
-                }
-        } else if (sftpRequest instanceof SshFxpCloseRequest) {
-        	SshFxpCloseRequest sshFxpCloseRequest = (SshFxpCloseRequest) sftpRequest;
-                String handle = sshFxpCloseRequest.getHandleId();
-                try {
-                    Handle h = handles.get(handle);
-                    if (h == null) {
-                        sendStatus(id, SSH_FX_INVALID_HANDLE, handle, "");
-                    } else {
-                        handles.remove(handle);
-                        h.close();
-                        sendStatus(id, SSH_FX_OK, "", "");
-                    }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                }
-        } else if (sftpRequest instanceof SshFxpReadRequest) {
-        	SshFxpReadRequest sshFxpReadRequest = (SshFxpReadRequest) sftpRequest;
-                String handle = sshFxpReadRequest.getHandleId();
-                long offset = sshFxpReadRequest.getOffset();
-                int len = sshFxpReadRequest.getLen();
-                try {
-                    Handle p = handles.get(handle);
-                    if (!(p instanceof FileHandle)) {
-                        sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
-                    } else {
-                        FileHandle fh = (FileHandle) p;
-                        byte[] b = new byte[Math.min(len, 1024 * 32)];
-                        len = fh.read(b, offset);
-                        if (len >= 0) {
-                        	SshFxpDataReply sftpReply = new SshFxpDataReply(id, b);
-                            Buffer buf = new Buffer(len + 5);
-                            buf.putByte((byte) SSH_FXP_DATA);
-                            buf.putInt(id);
-                            buf.putBytes(b, 0, len);
-                            if (version >= 6) {
-                                boolean lenFlag = len == 0;
-								buf.putBoolean(lenFlag);
-                                sftpReply = new SshFxpDataReply(id, Arrays.copyOf(b, len), lenFlag);
-                            }
-							if (processAfterCommand(session, sftpRequest, sftpReply)) return;
-                            send(buf);
-                        } else {
-                            sendStatus(id, SSH_FX_EOF, "");
-                        }
-                    }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                }
-        } else if (sftpRequest instanceof SshFxpWriteRequest) {
-        	SshFxpWriteRequest sshFxpWriteRequest = (SshFxpWriteRequest) sftpRequest;
-                String handle = sshFxpWriteRequest.getHandleId();
-                long offset = sshFxpWriteRequest.getOffset();
-                byte[] data = sshFxpWriteRequest.getData();
-                try {
-                    Handle p = handles.get(handle);
-                    if (!(p instanceof FileHandle)) {
-                        sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
+    private Reply doProcessRealpath(SshFxpRealpathRequest request) throws IOException {
+        int id = request.getId();
+        String path = request.getPath();
+        if (path.trim().length() == 0) {
+            path = ".";
+        }
+        // TODO: handle optional args
+        SshFile p = resolveFile(path);
+        if (!p.doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
+        }
+        String normalizedPath = SelectorUtils.normalizePath(p.getAbsolutePath(), "/");
+        if (normalizedPath.length() == 0) {
+            normalizedPath = "/";
+        }
+        p = resolveFile(normalizedPath);
+        if (p.getName().length() == 0) {
+            p = resolveFile(".");
+        }
+        SshFxpNameReply reply = new SshFxpNameReply(id);
+        int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
+        reply.addFile(p, normalizedPath, getLongName(p), new FileAttributes(p, flags));
+        return reply;
+    }
+
+    private Reply doProcessRmdir(SshFxpRmdirRequest request) throws IOException {
+        int id = request.getId();
+        String path = request.getPath();
+        // attrs
+        SshFile p = resolveFile(path);
+        if (p.isDirectory()) {
+            if (p.doesExist()) {
+                if (p.listSshFiles().size() == 0) {
+                    if (p.delete()) {
+                        return new SshFxpStatusReply(id, SSH_FX_OK, "");
                     } else {
-                        FileHandle fh = (FileHandle) p;
-                        fh.write(data, offset);
-                        SshFile sshFile = fh.getFile();
-
-                        sshFile.setLastModified(new Date().getTime());
-                        
-                        sendStatus(id, SSH_FX_OK, "");
+                        return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Unable to delete directory " + path);
                     }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                }
-        } else if ((sftpRequest instanceof SshFxpLstatRequest)
-        		 || (sftpRequest instanceof SshFxpStatRequest)) {
-        	String path;
-        	if (sftpRequest instanceof SshFxpLstatRequest) {
-        		SshFxpLstatRequest sshFxpLstatRequest = (SshFxpLstatRequest) sftpRequest;
-        		path = sshFxpLstatRequest.getPath();
-        	} else {
-        		SshFxpStatRequest sshFxpStatRequest = (SshFxpStatRequest) sftpRequest;
-        		path = sshFxpStatRequest.getPath();
-        	}
-                try {
-                    SshFile p = resolveFile(path);
-                    sendAttrs(id, p);
-                } catch (FileNotFoundException e) {
-                    sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
+                } else {
+                    return new SshFxpStatusReply(id, SSH_FX_DIR_NOT_EMPTY, path);
                 }
-        } else if (sftpRequest instanceof SshFxpFstatRequest) {
-        	SshFxpFstatRequest sshFxpFstatRequest = (SshFxpFstatRequest) sftpRequest;
-                String handle = sshFxpFstatRequest.getHandleId();
-                try {
-                    Handle p = handles.get(handle);
-                    if (p == null) {
-                        sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
-                    } else {
-                        sendAttrs(id, p.getFile());
-                    }
-                } catch (FileNotFoundException e) {
-                    sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
+            } else {
+                return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_PATH, path);
+            }
+        } else {
+            return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath());
+        }
+    }
+
+    private Reply doProcessMkdir(SshFxpMkdirRequest request) throws IOException {
+        int id = request.getId();
+        String path = request.getPath();
+        // attrs
+        SshFile p = resolveFile(path);
+        if (p.doesExist()) {
+            if (p.isDirectory()) {
+                return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, p.getAbsolutePath());
+            } else {
+                return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath());
+            }
+        } else if (!p.isWritable()) {
+            return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, p.getAbsolutePath());
+        } else if (!p.mkdir()) {
+            return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Error creating dir " + path);
+        } else {
+            return new SshFxpStatusReply(id, SSH_FX_OK, "");
+        }
+    }
+
+    private Reply doProcessRemove(SshFxpRemoveRequest request) throws IOException {
+        int id = request.getId();
+        String path = request.getPath();
+        SshFile p = resolveFile(path);
+        if (!p.doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
+        } else if (p.isDirectory()) {
+            return new SshFxpStatusReply(id, SSH_FX_FILE_IS_A_DIRECTORY, p.getAbsolutePath());
+        } else if (!p.delete()) {
+            return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Failed to delete file");
+        } else {
+            return new SshFxpStatusReply(id, SSH_FX_OK, "");
+        }
+    }
+
+    private Reply doProcessReaddir(SshFxpReaddirRequest request) throws IOException {
+        int id = request.getId();
+        String handle = request.getHandleId();
+        Handle p = getHandle(handle);
+        if (!(p instanceof DirectoryHandle)) {
+            return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
+        } else if (((DirectoryHandle) p).isDone()) {
+            return new SshFxpStatusReply(id, SSH_FX_EOF, "", "");
+        } else if (!p.getFile().doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath());
+        } else if (!p.getFile().isDirectory()) {
+            return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, p.getFile().getAbsolutePath());
+        } else if (!p.getFile().isReadable()) {
+            return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, p.getFile().getAbsolutePath());
+        } else {
+            DirectoryHandle dh = (DirectoryHandle) p;
+            if (dh.hasNext()) {
+                // There is at least one file in the directory.
+                // Send only a few files at a time to not create packets of a too
+                // large size or have a timeout to occur.
+                Reply reply = sendName(id, dh);
+                if (!dh.hasNext()) {
+                    // if no more files to send
+                    dh.setDone(true);
+                    dh.clearFileList();
                 }
-        } else if (sftpRequest instanceof SshFxpOpendirRequest) {
-        	SshFxpOpendirRequest sshFxpOpendirRequest = (SshFxpOpendirRequest) sftpRequest;
-                String path = sshFxpOpendirRequest.getPath();
-                try {
-                    SshFile p = resolveFile(path);
-                    if (!p.doesExist()) {
-                        sendStatus(id, SSH_FX_NO_SUCH_FILE, path);
-                    } else if (!p.isDirectory()) {
-                        sendStatus(id, SSH_FX_NOT_A_DIRECTORY, path);
-                    } else if (!p.isReadable()) {
-                        sendStatus(id, SSH_FX_PERMISSION_DENIED, path);
-                    } else {
-                        String handle = UUID.randomUUID().toString();
-                        handles.put(handle, new DirectoryHandle(p));
-                        sendHandle(id, handle);
-                    }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
+                return reply;
+            } else {
+                // empty directory
+                dh.setDone(true);
+                dh.clearFileList();
+                return new SshFxpStatusReply(id, SSH_FX_EOF, "", "");
+            }
+        }
+    }
+
+    private Reply doProcessOpendir(SshFxpOpendirRequest request) throws IOException {
+        int id = request.getId();
+        String path = request.getPath();
+        SshFile p = resolveFile(path);
+        if (!p.doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, path);
+        } else if (!p.isDirectory()) {
+            return new SshFxpStatusReply(id, SSH_FX_NOT_A_DIRECTORY, path);
+        } else if (!p.isReadable()) {
+            return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, path);
+        } else {
+            Handle handle = createDirectoryHandle(p);
+            return new SshFxpHandleReply(id, handle);
+        }
+    }
+
+    private Reply doProcessFstat(SshFxpFstatRequest request) throws IOException {
+        int id = request.getId();
+        String handle = request.getHandleId();
+        Handle p = getHandle(handle);
+        if (p == null) {
+            return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
+        } else {
+            int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
+            return new SshFxpAttrsReply(id, new FileAttributes(p.getFile(), flags));
+        }
+    }
+
+    private Reply doProcessStat(Request sftpRequest) throws IOException {
+        int id = sftpRequest.getId();
+        String path;
+        if (sftpRequest instanceof SshFxpLstatRequest) {
+            SshFxpLstatRequest sshFxpLstatRequest = (SshFxpLstatRequest) sftpRequest;
+            path = sshFxpLstatRequest.getPath();
+        } else {
+            SshFxpStatRequest sshFxpStatRequest = (SshFxpStatRequest) sftpRequest;
+            path = sshFxpStatRequest.getPath();
+        }
+        SshFile p = resolveFile(path);
+        if (!p.doesExist()) {
+            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
+        }
+        int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
+        return new SshFxpAttrsReply(id, new FileAttributes(p, flags));
+    }
+
+    private Reply doProcessWrite(SshFxpWriteRequest request) throws IOException {
+        int id = request.getId();
+        String handle = request.getHandleId();
+        long offset = request.getOffset();
+        byte[] data = request.getData();
+        Handle p = getHandle(handle);
+        if (!(p instanceof FileHandle)) {
+            return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
+        } else {
+            FileHandle fh = (FileHandle) p;
+            fh.write(data, offset);
+            SshFile sshFile = fh.getFile();
+
+            sshFile.setLastModified(new Date().getTime());
+
+            return new SshFxpStatusReply(id, SSH_FX_OK, "");
+        }
+    }
+
+    private Reply doProcessRead(SshFxpReadRequest request) throws IOException {
+        int id = request.getId();
+        String handle = request.getHandleId();
+        long offset = request.getOffset();
+        int len = request.getLength();
+        Handle p = getHandle(handle);
+        if (!(p instanceof FileHandle)) {
+            return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle);
+        } else {
+            FileHandle fh = (FileHandle) p;
+            byte[] b = new byte[Math.min(len, 1024 * 32)];
+            len = fh.read(b, offset);
+            if (len >= 0) {
+                return new SshFxpDataReply(id, b, 0, len, len < b.length);
+            } else {
+                return new SshFxpStatusReply(id, SSH_FX_EOF, "");
+            }
+        }
+    }
+
+    private Reply doProcessClose(SshFxpCloseRequest sftpRequest) throws IOException {
+        int id = sftpRequest.getId();
+        SshFxpCloseRequest sshFxpCloseRequest = (SshFxpCloseRequest) sftpRequest;
+        String handle = sshFxpCloseRequest.getHandleId();
+        Handle h = getHandle(handle);
+        if (h == null) {
+            return new SshFxpStatusReply(id, SSH_FX_INVALID_HANDLE, handle, "");
+        } else {
+            handles.remove(handle);
+            h.close();
+            return new SshFxpStatusReply(id, SSH_FX_OK, "", "");
+        }
+    }
+
+    private Reply doProcessOpen(SshFxpOpenRequest request) throws IOException {
+        int id = request.getId();
+        if (session.getFactoryManager().getProperties() != null) {
+            String maxHandlesString = session.getFactoryManager().getProperties().get(MAX_OPEN_HANDLES_PER_SESSION);
+            if (maxHandlesString != null) {
+                int maxHandleCount = Integer.parseInt(maxHandlesString);
+                if (handles.size() > maxHandleCount) {
+                    return new SshFxpStatusReply(id, SSH_FX_FAILURE, "Too many open handles");
                 }
-        } else if (sftpRequest instanceof SshFxpReaddirRequest) {
-        	SshFxpReaddirRequest sshFxpReaddirRequest = (SshFxpReaddirRequest) sftpRequest;
-                String handle = sshFxpReaddirRequest.getHandleId();
-                try {
-                    Handle p = handles.get(handle);
-                    if (!(p instanceof DirectoryHandle)) {
-                        sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
-                    } else if (((DirectoryHandle) p).isDone()) {
-                        sendStatus(id, SSH_FX_EOF, "", "");
-                    } else if (!p.getFile().doesExist()) {
-                        sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getFile().getAbsolutePath());
-                    } else if (!p.getFile().isDirectory()) {
-                        sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getFile().getAbsolutePath());
-                    } else if (!p.getFile().isReadable()) {
-                        sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getFile().getAbsolutePath());
-                    } else {
-                        DirectoryHandle dh = (DirectoryHandle) p;
-                        if (dh.hasNext()) {
-                            // There is at least one file in the directory.
-                            // Send only a few files at a time to not create packets of a too
-                            // large size or have a timeout to occur.
-                            sendName(id, dh);
-                            if (!dh.hasNext()) {
-                                // if no more files to send
-                                dh.setDone(true);
-                                dh.clearFileList();
-                            }
-                        } else {
-                            // empty directory
-                            dh.setDone(true);
-                            dh.clearFileList();
-                            sendStatus(id, SSH_FX_EOF, "", "");
-                        }
-                    }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
+            }
+        }
+
+        int accValue = request.getAcc();
+        if (accValue == 0) {
+            String path = request.getPath();
+            int flags = request.getFlags();
+            // attrs
+            SshFile file = resolveFile(path);
+            if (file.doesExist()) {
+                if (((flags & SSH_FXF_CREAT) != 0) && ((flags & SSH_FXF_EXCL) != 0)) {
+                    return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, path);
                 }
-        } else if (sftpRequest instanceof SshFxpRemoveRequest) {
-        	SshFxpRemoveRequest sshFxpRemoveRequest = (SshFxpRemoveRequest) sftpRequest;
-                String path = sshFxpRemoveRequest.getPath();
-                try {
-                    SshFile p = resolveFile(path);
-                    if (!p.doesExist()) {
-                        sendStatus(id, SSH_FX_NO_SUCH_FILE, p.getAbsolutePath());
-                    } else if (p.isDirectory()) {
-                        sendStatus(id, SSH_FX_FILE_IS_A_DIRECTORY, p.getAbsolutePath());
-                    } else if (!p.delete()) {
-                        sendStatus(id, SSH_FX_FAILURE, "Failed to delete file");
-                    } else {
-                        sendStatus(id, SSH_FX_OK, "");
+            } else {
+                if ((flags & SSH_FXF_CREAT) != 0) {
+                    if (!file.isWritable()) {
+                        return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
                     }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
+                    file.create();
                 }
-        } else if (sftpRequest instanceof SshFxpMkdirRequest) {
-        	SshFxpMkdirRequest sshFxpMkdirRequest = (SshFxpMkdirRequest) sftpRequest;
-                String path = sshFxpMkdirRequest.getPath();
-                // attrs
-                try {
-                    SshFile p = resolveFile(path);
-                    if (p.doesExist()) {
-                        if (p.isDirectory()) {
-                            sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, p.getAbsolutePath());
+            }
+            if ((flags & SSH_FXF_TRUNC) != 0) {
+                file.truncate();
+            }
+            return new SshFxpHandleReply(id, createFileHandle(file, flags));
+        } else {
+            String path = request.getPath();
+            int acc = accValue;
+            int flags = request.getFlags();
+            // attrs
+            SshFile file = resolveFile(path);
+            switch (flags & SSH_FXF_ACCESS_DISPOSITION) {
+                case SSH_FXF_CREATE_NEW: {
+                    if (file.doesExist()) {
+                        return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, path);
+                    } else if (!file.isWritable()) {
+                        return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
+                    }
+                    file.create();
+                    break;
+                }
+                case SSH_FXF_CREATE_TRUNCATE: {
+                    if (file.doesExist()) {
+                        return new SshFxpStatusReply(id, SSH_FX_FILE_ALREADY_EXISTS, path);
+                    } else if (!file.isWritable()) {
+                        return new SshFxpStatusReply(id, SSH_FX_PERMISSION_DENIED, "Can not create " + path);
+                    }
+                    file.truncate();
+                    break;
+                }
+                case SSH_FXF_OPEN_EXISTING: {
+                    if (!file.doesExist()) {
+                        if (!file.getParentFile().doesExist()) {
+                            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_PATH, path);
                         } else {
-                            sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath());
+                            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, path);
                         }
-                    } else if (!p.isWritable()) {
-                        sendStatus(id, SSH_FX_PERMISSION_DENIED, p.getAbsolutePath());
-                    } else if (!p.mkdir()) {
-                        throw new IOException("Error creating dir " + path);
-                    } else {
-                        sendStatus(id, SSH_FX_OK, "");
                     }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
+                    break;
                 }
-        } else if (sftpRequest instanceof SshFxpRmdirRequest) {
-        	SshFxpRmdirRequest sshFxpRmdirRequest = (SshFxpRmdirRequest) sftpRequest;
-                String path = sshFxpRmdirRequest.getPath();
-                // attrs
-                try {
-                    SshFile p = resolveFile(path);
-                    if (p.isDirectory()) {
-                        if (p.doesExist()) {
-                            if (p.listSshFiles().size() == 0) {
-                                if (p.delete()) {
-                                    sendStatus(id, SSH_FX_OK, "");
-                                } else {
-                                    sendStatus(id, SSH_FX_FAILURE, "Unable to delete directory " + path);
-                                }
-                            } else {
-                                sendStatus(id, SSH_FX_DIR_NOT_EMPTY, path);
-                            }
+                case SSH_FXF_OPEN_OR_CREATE: {
+                    if (!file.doesExist()) {
+                        file.create();
+                    }
+                    break;
+                }
+                case SSH_FXF_TRUNCATE_EXISTING: {
+                    if (!file.doesExist()) {
+                        if (!file.getParentFile().doesExist()) {
+                            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_PATH, path);
                         } else {
-                            sendStatus(id, SSH_FX_NO_SUCH_PATH, path);
+                            return new SshFxpStatusReply(id, SSH_FX_NO_SUCH_FILE, path);
                         }
-                    } else {
-                        sendStatus(id, SSH_FX_NOT_A_DIRECTORY, p.getAbsolutePath());
                     }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                }
-        } else if (sftpRequest instanceof SshFxpRealpathRequest) {
-        	SshFxpRealpathRequest sshFxpRealpathRequest = (SshFxpRealpathRequest) sftpRequest;
-                String path = sshFxpRealpathRequest.getPath();
-                if (path.trim().length() == 0) {
-                    path = ".";
+                    file.truncate();
+                    break;
                 }
-                // TODO: handle optional args
-                try {
-                    SshFile p = resolveFile(path);
-                    sendPath(id, p);
-                } catch (FileNotFoundException e) {
-                	log.error("error while resove file, cause: " + e.getMessage());
-                    sendStatus(id, SSH_FX_NO_SUCH_FILE, e.getMessage());
-                } catch (IOException e) {
-                	log.error("error while resove file, cause: " + e.getMessage());
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                }
-        } else if (sftpRequest instanceof SshFxpRenameRequest) {
-        	SshFxpRenameRequest sshFxpRenameRequest = (SshFxpRenameRequest) sftpRequest;
-                String oldPath = sshFxpRenameRequest.getOldPath();
-                String newPath = sshFxpRenameRequest.getNewPath();
-                try {
-                    SshFile o = resolveFile(oldPath);
-                    SshFile n = resolveFile(newPath);
-                    if (!o.doesExist()) {
-                        sendStatus(id, SSH_FX_NO_SUCH_FILE, o.getAbsolutePath());
-                    } else if (n.doesExist()) {
-                        sendStatus(id, SSH_FX_FILE_ALREADY_EXISTS, n.getAbsolutePath());
-                    } else if (!o.move(n)) {
-                        sendStatus(id, SSH_FX_FAILURE, "Failed to rename file");
-                    } else {
-                        sendStatus(id, SSH_FX_OK, "");
-                    }
-                } catch (IOException e) {
-                    sendStatus(id, SSH_FX_FAILURE, e.getMessage());
-                }
-        } else if ((sftpRequest instanceof SshFxpSetstatRequest)
-        		 || (sftpRequest instanceof SshFxpFsetstatRequest)) {
-                // This is required for WinSCP / Cyberduck to upload properly
-                // Blindly reply "OK"
-                // TODO implement it
-                sendStatus(id, SSH_FX_OK, "");
-        } else {
-        		log.error("Received: {}", sftpRequest);
-                sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command " + sftpRequest + " is unsupported or not implemented");
+                default:
+                    throw new IllegalArgumentException("Unsupported open mode: " + flags);
+            }
+            return new SshFxpHandleReply(id, createFileHandle(file, flags));
         }
     }
 
-    protected void sendHandle(int id, String handle) throws IOException {
-    	if (processAfterCommand(session, sftpRequest, new SshFxpHandleReply(id, handle, handles.get(handle)))) return;
-        Buffer buffer = new Buffer();
-        buffer.putByte((byte) SSH_FXP_HANDLE);
-        buffer.putInt(id);
-        buffer.putString(handle);
-        send(buffer);
-    }
-
-    protected void sendAttrs(int id, SshFile file) throws IOException {
-    	if (processAfterCommand(session, sftpRequest, new SshFxpAttrsReply(id, file))) return;
-        Buffer buffer = new Buffer();
-        buffer.putByte((byte) SSH_FXP_ATTRS);
-        buffer.putInt(id);
-        writeAttrs(buffer, file);
-        send(buffer);
-    }
-
-    protected void sendAttrs(int id, SshFile file, int flags) throws IOException {
-    	if (processAfterCommand(session, sftpRequest, new SshFxpAttrsReply(id, file, flags))) return;
-        Buffer buffer = new Buffer();
-        buffer.putByte((byte) SSH_FXP_ATTRS);
-        buffer.putInt(id);
-        writeAttrs(buffer, file, flags);
-        send(buffer);
+    private Reply doProcessInit(SshFxpInitRequest request) throws IOException {
+        int id = request.getId();
+        version = id;
+        if (version >= LOWER_SFTP_IMPL) {
+            version = Math.min(version, HIGHER_SFTP_IMPL);
+            return new SshFxpVersionReply(version);
+        } else {
+            // We only support version >= 3 (Version 1 and 2 are not common)
+            return new SshFxpStatusReply(id, SSH_FX_OP_UNSUPPORTED, "SFTP server only support versions " + ALL_SFTP_IMPL);
+        }
     }
 
-
-    protected void sendPath(int id, SshFile f) throws IOException {
-        int count = 1;
-    	SshFxpNameReply sshFxpNameReply = new SshFxpNameReply(id, true);
-        Buffer buffer = new Buffer();
-        buffer.putByte((byte) SSH_FXP_NAME);
-        buffer.putInt(id);
-        buffer.putInt(count);
-        //normalize the given path, use *nix style separator
-        String normalizedPath = SelectorUtils.normalizePath(f.getAbsolutePath(), "/");
-        if (normalizedPath.length() == 0) {
-            normalizedPath = "/";
-        }
-        buffer.putString(normalizedPath);
-        f = resolveFile(normalizedPath);
-        if (f.getName().length() == 0) {
-            f = resolveFile(".");
-        }
-        String longName;
-        if (version <= 3) {
-            longName = getLongName(f);
-			buffer.putString(longName); // Format specified in the specs
-            buffer.putInt(0);
-            sshFxpNameReply.addFile(f, normalizedPath, longName, 0);
-        } else {
-            longName = f.getName();
-			buffer.putString(longName); // Supposed to be UTF-8
-            writeAttrs(buffer, f);
-            sshFxpNameReply.addFile(f, normalizedPath, longName, null);
-        }
-        
-    	if (processAfterCommand(session, sftpRequest, sshFxpNameReply)) return;
-        send(buffer);
-    }
-
-    protected void sendName(int id, Iterator<SshFile> files) throws IOException {
-    	SshFxpNameReply sshFxpNameReply = new SshFxpNameReply(id, false);
-        Buffer buffer = new Buffer();
-        buffer.putByte((byte) SSH_FXP_NAME);
-        buffer.putInt(id);
-        int wpos = buffer.wpos();
-        buffer.putInt(0);
+    protected SshFxpNameReply sendName(int id, Iterator<SshFile> files) throws IOException {
+        SshFxpNameReply reply = new SshFxpNameReply(id);
         int nb = 0;
-        while (files.hasNext() && buffer.wpos() < MAX_PACKET_LENGTH) {
+        while (files.hasNext() && nb < MAX_PACKET_LENGTH / 2) {
             SshFile f = files.next();
             String filename = f.getName();
-            buffer.putString(filename);
-			String longname;
             if (version <= 3) {
-                longname = getLongName(f);
-				buffer.putString(longname); // Format specified in the specs
+                nb += 55 + filename.length() * 2;
             } else {
-                longname = f.getName();
-				buffer.putString(longname); // Supposed to be UTF-8
+                nb += filename.length();
             }
-            sshFxpNameReply.addFile(f, filename, longname, null);
-            writeAttrs(buffer, f);
-            nb++;
-        }
-        int oldpos = buffer.wpos();
-        buffer.wpos(wpos);
-        buffer.putInt(nb);
-        buffer.wpos(oldpos);
-		if (processAfterCommand(session, sftpRequest, sshFxpNameReply)) return;
-        send(buffer);
+            nb += 10; // Attrs size
+            int flags = SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_SIZE;
+            reply.addFile(f, filename, getLongName(f), new FileAttributes(f, flags));
+        }
+        reply.setEol(!files.hasNext());
+        return reply;
+    }
+
+    protected void send(Buffer buffer) throws IOException {
+        DataOutputStream dos = new DataOutputStream(out);
+        dos.writeInt(buffer.available());
+        dos.write(buffer.array(), buffer.rpos(), buffer.available());
+        dos.flush();
+    }
+
+    public void destroy() {
+        closed = true;
+    }
+
+    private SshFile resolveFile(String path) {
+        return this.root.getFile(path);
     }
 
+
     private String getLongName(SshFile f) {
         String username = f.getOwner();
         if (username.length() > 8) {
@@ -1038,111 +700,8 @@ public class SftpSubsystem implements Co
         return sb.toString();
     }
 
-    protected void writeAttrs(Buffer buffer, SshFile file) throws IOException {
-        writeAttrs(buffer, file, 0);
-    }
-
-
-    protected void writeAttrs(Buffer buffer, SshFile file, int flags) throws IOException {
-        if (!file.doesExist()) {
-            throw new FileNotFoundException(file.getAbsolutePath());
-        }
-        if (version >= 4) {
-            long size = file.getSize();
-            String username = session.getUsername();
-            long lastModif = file.getLastModified();
-            int p = 0;
-            if (file.isReadable()) {
-                p |= S_IRUSR;
-            }
-            if (file.isWritable()) {
-                p |= S_IWUSR;
-            }
-            if (file.isExecutable()) {
-                p |= S_IXUSR;
-            }
-            if (file.isFile()) {
-                buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS);
-                buffer.putByte((byte) SSH_FILEXFER_TYPE_REGULAR);
-                buffer.putInt(p);
-            } else if (file.isDirectory()) {
-                buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS);
-                buffer.putByte((byte) SSH_FILEXFER_TYPE_DIRECTORY);
-                buffer.putInt(p);
-            } else {
-                buffer.putInt(0);
-                buffer.putByte((byte) SSH_FILEXFER_TYPE_UNKNOWN);
-            }
-        } else {
-            int p = 0;
-            if (file.isFile()) {
-                p |= 0100000;
-            }
-            if (file.isDirectory()) {
-                p |= 0040000;
-            }
-            if (file.isReadable()) {
-                p |= 0000400;
-            }
-            if (file.isWritable()) {
-                p |= 0000200;
-            }
-            if (file.isExecutable()) {
-                p |= 0000100;
-            }
-            if (file.isFile()) {
-                buffer.putInt(SSH_FILEXFER_ATTR_SIZE| SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
-                buffer.putLong(file.getSize());
-                buffer.putInt(p);
-                buffer.putInt(file.getLastModified()/1000);
-                buffer.putInt(file.getLastModified()/1000);
-            } else if (file.isDirectory()) {
-                buffer.putInt(SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
-                buffer.putInt(p);
-                buffer.putInt(file.getLastModified()/1000);
-                buffer.putInt(file.getLastModified()/1000);
-            } else {
-                buffer.putInt(0);
-            }
-        }
-    }
-
-    protected void sendStatus(int id, int substatus, String msg) throws IOException {
-        sendStatus(id, substatus, msg, "");
-    }
-
-    protected void sendStatus(int id, int substatus, String msg, String lang) throws IOException {
-    	if (processAfterCommand(session, sftpRequest, new SshFxpStatusReply(id, substatus, msg, lang))) return;
-    	sendStatusStrict(id, substatus, msg, lang);
-    }
-
-    protected void sendStatusStrict(int id, int substatus, String msg, String lang) throws IOException {
-        Buffer buffer = new Buffer();
-        buffer.putByte((byte) SSH_FXP_STATUS);
-        buffer.putInt(id);
-        buffer.putInt(substatus);
-        buffer.putString(msg);
-        buffer.putString(lang);
-        send(buffer);
-    }
-
-    protected void send(Buffer buffer) throws IOException {
-        DataOutputStream dos = new DataOutputStream(out);
-        dos.writeInt(buffer.available());
-        dos.write(buffer.array(), buffer.rpos(), buffer.available());
-        dos.flush();
-    }
-
-    public void destroy() {
-        closed = true;
-    }
-
-    private SshFile resolveFile(String path) {
-    	return this.root.getFile(path);
-    }
-
-    private final static String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May",
-            "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+    private final static String[] MONTHS = {"Jan", "Feb", "Mar", "Apr", "May",
+            "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 
     /**
      * Get unix style date string.
@@ -1196,179 +755,4 @@ public class SftpSubsystem implements Co
         return sb.toString();
     }
 
-
-	protected Request getSftpRequest(final Buffer buffer) {
-		Request sftpRequest;
-
-		int length = buffer.getInt();
-        int type = buffer.getByte();
-        int id = buffer.getInt();
-        
-        switch (type) {
-        case SSH_FXP_INIT:
-            if (length != 5) {
-                throw new IllegalArgumentException();
-            }
-        	sftpRequest = new SshFxpInitRequest(id); 
-        	break;
-        	case SSH_FXP_OPEN:
-                if (version <= 4) {
-                    String path = buffer.getString();
-                    int pflags = buffer.getInt();
-                	sftpRequest = new SshFxpOpenRequest(id, path, pflags); 
-                } else {
-                    String path = buffer.getString();
-                    int acc = buffer.getInt();
-                    int flags = buffer.getInt();
-                	sftpRequest = new SshFxpOpenRequest(id, path, acc, flags); 
-                }
-        	break;
-        	case SSH_FXP_CLOSE: {
-                String handleId = buffer.getString();
-                Handle handle = handles.get(handleId);
-            	sftpRequest = new SshFxpCloseRequest(id, handleId, handle);
-        	} break;
-        	case SSH_FXP_READ: {
-                String handleId = buffer.getString();
-                long offset = buffer.getLong();
-                int len = buffer.getInt();
-                Handle handle = handles.get(handleId);
-            	sftpRequest = new SshFxpReadRequest(id, handleId, offset, len, handle); 
-        	} break;
-        	case SSH_FXP_WRITE: {
-                String handleId = buffer.getString();
-                long offset = buffer.getLong();
-                byte[] data = buffer.getBytes();
-                Handle handle = handles.get(handleId);
-            	sftpRequest = new SshFxpWriteRequest(id, handleId, offset, data, handle); 
-        	} break;
-        	case SSH_FXP_STAT: {
-                String path = buffer.getString();
-            	sftpRequest = new SshFxpStatRequest(id, path); 
-        	} break;
-        	case SSH_FXP_LSTAT: {
-                String path = buffer.getString();
-            	sftpRequest = new SshFxpLstatRequest(id, path); 
-        	} break;
-        	case SSH_FXP_FSTAT: {
-                String handle = buffer.getString();
-                Handle p = handles.get(handle);
-            	sftpRequest = new SshFxpFstatRequest(id, handle, p); 
-        	} break;
-        	case SSH_FXP_OPENDIR: {
-                String path = buffer.getString();
-                sftpRequest = new SshFxpOpendirRequest(id, path); 
-        	} break;
-        	case SSH_FXP_READDIR:
-                String handle = buffer.getString();
-                Handle p = handles.get(handle);
-            	sftpRequest = new SshFxpReaddirRequest(id, handle, p); 
-        	break;
-        	case SSH_FXP_REMOVE: {
-                String path = buffer.getString();
-            	sftpRequest = new SshFxpRemoveRequest(id, path); 
-        	} break;
-        	case SSH_FXP_MKDIR: {
-                String path = buffer.getString();
-            	sftpRequest = new SshFxpMkdirRequest(id, path); 
-        	} break;
-        	case SSH_FXP_RMDIR: {
-                String path = buffer.getString();
-            	sftpRequest = new SshFxpRmdirRequest(id, path); 
-        	} break;
-        	case SSH_FXP_REALPATH:
-                String path = buffer.getString();
-                sftpRequest = new SshFxpRealpathRequest(id, path); 
-        	break;
-        	case SSH_FXP_RENAME:
-                final String oldPath = buffer.getString();
-                final String newPath = buffer.getString();
-            	sftpRequest = new SshFxpRenameRequest(id, oldPath, newPath); 
-        	break;
-        	case SSH_FXP_SETSTAT:
-            	sftpRequest = new SshFxpSetstatRequest(id); 
-        	break;
-        	case SSH_FXP_FSETSTAT:
-            	sftpRequest = new SshFxpFsetstatRequest(id); 
-        	break;
-        	default:
-            	sftpRequest = new UnsupportedRequest(id, type); 
-        }
-        
-		return sftpRequest;
-	}
-
-    private boolean processAfterCommand(final ServerSession session2, final Request sftpRequest2, final Reply sftpReply)
-    		throws IOException {
-    	Reply reply = sftpLet.afterCommand(session2, sftpRequest2, sftpReply);
-
-    	if (reply == null) {
-    		return false;
-    	} else {
-    		sendReply(sftpReply);
-    		return true;
-    	}
-	}
-
-	private void sendReply(final Reply sftpReply) throws IOException {
-		if (sftpReply instanceof SshFxpAttrsReply) {
-			SshFxpAttrsReply sshFxpAttrsReply = (SshFxpAttrsReply) sftpReply;
-			int     id    = sshFxpAttrsReply.getId();
-			SshFile file  = sshFxpAttrsReply.getFile();
-			Integer     flags = sshFxpAttrsReply.getFlags();
-			if (flags == null) {
-				sendAttrs(id, file);
-			} else {
-				sendAttrs(id, file, flags);
-			}
-		} else if (sftpReply instanceof SshFxpDataReply) {
-			SshFxpDataReply sshFxpDataReply = (SshFxpDataReply) sftpReply;
-		    long    id      = sshFxpDataReply.getId();
-		    byte[]  data    = sshFxpDataReply.getData();
-		    Boolean lenflag = sshFxpDataReply.getLenFlag();
-
-		    int len = data.length;
-		    Buffer buf = new Buffer(len + 5);
-		    buf.putByte((byte) SSH_FXP_DATA);
-			buf.putInt(id);
-			buf.putBytes(data, 0, len);
-		    if (version >= 6) {
-		        buf.putBoolean(lenflag);
-		    }
-		    send(buf);
-		} else if (sftpReply instanceof SshFxpHandleReply) {
-			SshFxpHandleReply sshFxpHandleReply = (SshFxpHandleReply) sftpReply;
-			int    id     = sshFxpHandleReply.getId();
-			String handle = sshFxpHandleReply.getHandle();
-			
-			sendHandle(id, handle);
-		} else if (sftpReply instanceof SshFxpNameReply) {
-			SshFxpNameReply sshFxpNameReply = (SshFxpNameReply) sftpReply;
-			int             id    = sshFxpNameReply.getId();
-			
-			Iterator<SshFile> files = sshFxpNameReply.getFiles();
-			
-			if (sshFxpNameReply.isSendPath()) {
-				SshFile file = files.next();
-				sendPath(id, file);
-			} else {
-				sendName(id, files);
-			}
-		} else if (sftpReply instanceof SshFxpStatusReply) {
-			SshFxpStatusReply sshFxpStatusReply = (SshFxpStatusReply) sftpReply;
-			
-			int id = sshFxpStatusReply.getId();
-			int substatus = sshFxpStatusReply.getSubstatus();
-			String msg = sshFxpStatusReply.getMsg();
-			String lang = sshFxpStatusReply.getLang();
-			sendStatusStrict(id, substatus, msg, lang);
-		} else if (sftpReply instanceof SshFxpVersionReply) {
-			SshFxpVersionReply sshFxpVersionReply = (SshFxpVersionReply) sftpReply;
-	        Buffer buffer = new Buffer();
-			buffer.putByte((byte) SftpSubsystem.SSH_FXP_VERSION);
-			int version = sshFxpVersionReply.getVersion();
-			buffer.putInt(version);
-			send(buffer);
-		}
-	}
 }

Added: mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java?rev=1434658&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java (added)
+++ mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/SftpTest.java Thu Jan 17 13:09:51 2013
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.sftp;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.Logger;
+import com.jcraft.jsch.UserInfo;
+import org.apache.sshd.SshServer;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.command.ScpCommandFactory;
+import org.apache.sshd.sftp.subsystem.SftpSubsystem;
+import org.apache.sshd.sftp.util.BogusPasswordAuthenticator;
+import org.apache.sshd.sftp.util.EchoShellFactory;
+import org.apache.sshd.sftp.util.Utils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.*;
+import java.net.ServerSocket;
+import java.util.Arrays;
+import java.util.Vector;
+
+import static org.junit.Assert.*;
+
+public class SftpTest {
+
+    private SshServer sshd;
+    private int port;
+    private com.jcraft.jsch.Session session;
+
+    @Before
+    public void setUp() throws Exception {
+        ServerSocket s = new ServerSocket(0);
+        port = s.getLocalPort();
+        s.close();
+
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setPort(port);
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setCommandFactory(new ScpCommandFactory());
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
+        sshd.start();
+
+        JSch sch = new JSch();
+        sch.setLogger(new Logger() {
+            public boolean isEnabled(int i) {
+                return true;
+            }
+
+            public void log(int i, String s) {
+                System.out.println("Log(jsch," + i + "): " + s);
+            }
+        });
+        session = sch.getSession("sshd", "localhost", port);
+        session.setUserInfo(new UserInfo() {
+            public String getPassphrase() {
+                return null;
+            }
+
+            public String getPassword() {
+                return "sshd";
+            }
+
+            public boolean promptPassword(String message) {
+                return true;
+            }
+
+            public boolean promptPassphrase(String message) {
+                return false;
+            }
+
+            public boolean promptYesNo(String message) {
+                return true;
+            }
+
+            public void showMessage(String message) {
+            }
+        });
+        session.connect();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        session.disconnect();
+        sshd.stop();
+    }
+
+    @Test
+    @Ignore
+    public void testExternal() throws Exception {
+        System.out.println("SFTP subsystem available on port " + port);
+        Thread.sleep(5 * 60000);
+    }
+
+    @Test
+    public void testSftp() throws Exception {
+        String d = "0123456789\n";
+
+        File root = new File("target/scp");
+        File target = new File("target/scp/out.txt");
+        root.mkdirs();
+        assertTrue(root.exists());
+
+        for (int j = 10; j <= 10; j++) {
+            String data = "";
+            for (int i = 0; i < j; i++) {
+                data = data + d;
+            }
+
+            target.delete();
+            assertFalse(target.exists());
+            sendFile("target/scp/out.txt", data);
+            assertFileLength(target, data.length(), 5000);
+
+            target.delete();
+            assertFalse(target.exists());
+        }
+        root.delete();
+    }
+
+    @Test
+    public void testReadWriteWithOffset() throws Exception {
+        File root = new File("target/scp");
+        String unixPath = "target/scp/out.txt";
+        File target = new File(unixPath);
+        root.mkdirs();
+        assertTrue(root.exists());
+
+        ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
+        c.connect();
+        c.put(new ByteArrayInputStream("0123456789".getBytes()), unixPath);
+
+        assertTrue(target.exists());
+        assertEquals("0123456789", readFile(unixPath));
+
+        OutputStream os = c.put(unixPath, null, ChannelSftp.APPEND, -5);
+        os.write("a".getBytes());
+        os.close();
+        c.disconnect();
+
+        assertTrue(target.exists());
+        assertEquals("01234a", readFile(unixPath));
+
+        target.delete();
+        assertFalse(target.exists());
+        root.delete();
+    }
+
+    @Test
+    public void testReadDir() throws Exception {
+        ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
+        c.connect();
+        Vector res = c.ls("target/classes/org/apache/sshd/");
+        for (Object f : res) {
+            System.out.println(f.toString());
+        }
+    }
+
+    protected void assertFileLength(File file, long length, long timeout) throws Exception {
+        boolean ok = false;
+        while (timeout > 0) {
+            if (file.exists() && file.length() == length) {
+                if (!ok) {
+                    ok = true;
+                } else {
+                    return;
+                }
+            } else {
+                ok = false;
+            }
+            Thread.sleep(100);
+            timeout -= 100;
+        }
+        assertTrue(file.exists());
+        assertEquals(length, file.length());
+    }
+
+    protected String readFile(String path) throws Exception {
+        ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
+        c.connect();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        InputStream is = c.get(path);
+        try {
+            byte[] buffer = new byte[256];
+            int count;
+            while (-1 != (count = is.read(buffer))) {
+                bos.write(buffer, 0, count);
+            }
+        } finally {
+            is.close();
+        }
+
+        c.disconnect();
+        return new String(bos.toByteArray());
+    }
+
+    protected void sendFile(String path, String data) throws Exception {
+        ChannelSftp c = (ChannelSftp) session.openChannel("sftp");
+        c.connect();
+        c.put(new ByteArrayInputStream(data.getBytes()), path);
+        c.disconnect();
+    }
+
+}

Added: mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java?rev=1434658&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java (added)
+++ mina/sshd/trunk/sshd-sftp/src/test/java/org/apache/sshd/sftp/util/BogusPasswordAuthenticator.java Thu Jan 17 13:09:51 2013
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.sftp.util;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BogusPasswordAuthenticator implements PasswordAuthenticator {
+
+    public boolean authenticate(String username, String password, ServerSession session) {
+        return username != null && username.equals(password);
+    }
+}