http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/NewlineParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/NewlineParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/NewlineParser.java
new file mode 100644
index 0000000..2ad7ddb
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/NewlineParser.java
@@ -0,0 +1,115 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.subsystem.sftp.extensions.NewlineParser.Newline;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class NewlineParser extends AbstractParser<Newline> {
+    /**
+     * The &quot;newline&quot; extension information as per
+     * <A 
HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt";>DRAFT
 09 Section 4.3</A>
+     *
+     * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD 
Project</a>
+     */
+    public static class Newline implements Cloneable, Serializable {
+        private static final long serialVersionUID = 2010656704254497899L;
+        private String newline;
+
+        public Newline() {
+            this(null);
+        }
+
+        public Newline(String newline) {
+            this.newline = newline;
+        }
+
+        public String getNewline() {
+            return newline;
+        }
+
+        public void setNewline(String newline) {
+            this.newline = newline;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(getNewline());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (obj == this) {
+                return true;
+            }
+            if (obj.getClass() != getClass()) {
+                return false;
+            }
+
+            return Objects.equals(((Newline) obj).getNewline(), getNewline());
+        }
+
+        @Override
+        public Newline clone() {
+            try {
+                return getClass().cast(super.clone());
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("Failed to clone " + toString() + 
": " + e.getMessage(), e);
+            }
+        }
+
+        @Override
+        public String toString() {
+            String nl = getNewline();
+            if (GenericUtils.isEmpty(nl)) {
+                return nl;
+            } else {
+                return BufferUtils.toHex(':', 
nl.getBytes(StandardCharsets.UTF_8));
+            }
+        }
+    }
+
+    public static final NewlineParser INSTANCE = new NewlineParser();
+
+    public NewlineParser() {
+        super(SftpConstants.EXT_NEWLINE);
+    }
+
+    @Override
+    public Newline parse(byte[] input, int offset, int len) {
+        return parse(new String(input, offset, len, StandardCharsets.UTF_8));
+    }
+
+    public Newline parse(String value) {
+        return new Newline(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
new file mode 100644
index 0000000..e565ab4
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/ParserUtils.java
@@ -0,0 +1,195 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import 
org.apache.sshd.common.subsystem.sftp.extensions.Supported2Parser.Supported2;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.SupportedParser.Supported;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.openssh.FstatVfsExtensionParser;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.openssh.FsyncExtensionParser;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.openssh.HardLinkExtensionParser;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.openssh.PosixRenameExtensionParser;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.openssh.StatVfsExtensionParser;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ * @see <A 
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL";>OpenSSH 
-  section 3.4</A>
+ */
+public final class ParserUtils {
+    public static final Collection<ExtensionParser<?>> BUILT_IN_PARSERS =
+            Collections.unmodifiableList(
+                    Arrays.<ExtensionParser<?>>asList(
+                            VendorIdParser.INSTANCE,
+                            NewlineParser.INSTANCE,
+                            VersionsParser.INSTANCE,
+                            SupportedParser.INSTANCE,
+                            Supported2Parser.INSTANCE,
+                            AclSupportedParser.INSTANCE,
+                            // OpenSSH extensions
+                            PosixRenameExtensionParser.INSTANCE,
+                            StatVfsExtensionParser.INSTANCE,
+                            FstatVfsExtensionParser.INSTANCE,
+                            HardLinkExtensionParser.INSTANCE,
+                            FsyncExtensionParser.INSTANCE
+                    ));
+
+    private static final Map<String, ExtensionParser<?>> PARSERS_MAP;
+
+    static {
+        PARSERS_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (ExtensionParser<?> p : BUILT_IN_PARSERS) {
+            PARSERS_MAP.put(p.getName(), p);
+        }
+    }
+
+    private ParserUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param parser The {@link ExtensionParser} to register
+     * @return The replaced parser (by name) - {@code null} if no previous 
parser
+     * for this extension name
+     */
+    public static ExtensionParser<?> registerParser(ExtensionParser<?> parser) 
{
+        Objects.requireNonNull(parser, "No parser instance");
+
+        synchronized (PARSERS_MAP) {
+            return PARSERS_MAP.put(parser.getName(), parser);
+        }
+    }
+
+    /**
+     * @param name The extension name - ignored if {@code null}/empty
+     * @return The removed {@link ExtensionParser} - {@code null} if none 
registered
+     * for this extension name
+     */
+    public static ExtensionParser<?> unregisterParser(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        synchronized (PARSERS_MAP) {
+            return PARSERS_MAP.remove(name);
+        }
+    }
+
+    /**
+     * @param name The extension name - ignored if {@code null}/empty
+     * @return The registered {@link ExtensionParser} - {@code null} if none 
registered
+     * for this extension name
+     */
+    public static ExtensionParser<?> getRegisteredParser(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        synchronized (PARSERS_MAP) {
+            return PARSERS_MAP.get(name);
+        }
+    }
+
+    public static Set<String> getRegisteredParsersNames() {
+        synchronized (PARSERS_MAP) {
+            if (PARSERS_MAP.isEmpty()) {
+                return Collections.emptySet();
+            } else {    // return a copy in order to avoid concurrent 
modification issues
+                return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, 
PARSERS_MAP.keySet());
+            }
+        }
+    }
+
+    public static List<ExtensionParser<?>> getRegisteredParsers() {
+        synchronized (PARSERS_MAP) {
+            if (PARSERS_MAP.isEmpty()) {
+                return Collections.emptyList();
+            } else { // return a copy in order to avoid concurrent 
modification issues
+                return new ArrayList<>(PARSERS_MAP.values());
+            }
+        }
+    }
+
+    public static Set<String> supportedExtensions(Map<String, ?> parsed) {
+        if (GenericUtils.isEmpty(parsed)) {
+            return Collections.emptySet();
+        }
+
+        Supported sup = (Supported) 
parsed.get(SupportedParser.INSTANCE.getName());
+        Collection<String> extra = (sup == null) ? null : sup.extensionNames;
+        Supported2 sup2 = (Supported2) 
parsed.get(Supported2Parser.INSTANCE.getName());
+        Collection<String> extra2 = (sup2 == null) ? null : 
sup2.extensionNames;
+        if (GenericUtils.isEmpty(extra)) {
+            return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, 
extra2);
+        } else if (GenericUtils.isEmpty(extra2)) {
+            return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, 
extra);
+        }
+
+        Set<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+        result.addAll(extra);
+        result.addAll(extra2);
+        return result;
+    }
+
+    /**
+     * @param extensions The received extensions in encoded form
+     * @return A {@link Map} of all the successfully decoded extensions
+     * where key=extension name (same as in the original map), value=the
+     * decoded extension value. Extensions for which there is no registered
+     * parser are <U>ignored</U>
+     * @see #getRegisteredParser(String)
+     * @see ExtensionParser#parse(byte[])
+     */
+    public static Map<String, Object> parse(Map<String, byte[]> extensions) {
+        if (GenericUtils.isEmpty(extensions)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, Object> data = new 
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        extensions.forEach((name, value) -> {
+            Object result = parse(name, value);
+            if (result == null) {
+                return;
+            }
+            data.put(name, result);
+        });
+
+        return data;
+    }
+
+    public static Object parse(String name, byte... encoded) {
+        ExtensionParser<?> parser = getRegisteredParser(name);
+        if (parser == null) {
+            return null;
+        } else {
+            return parser.parse(encoded);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
new file mode 100644
index 0000000..16dc184
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
@@ -0,0 +1,125 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import java.io.IOException;
+import java.nio.file.FileStore;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ * @see <A 
HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt";>DRAFT
 09 section 9.2</A>
+ */
+public class SpaceAvailableExtensionInfo implements Cloneable {
+    // CHECKSTYLE:OFF
+    public long bytesOnDevice;
+    public long unusedBytesOnDevice;
+    public long bytesAvailableToUser;
+    public long unusedBytesAvailableToUser;
+    public int bytesPerAllocationUnit;
+    // CHECKSTYLE:ON
+
+    public SpaceAvailableExtensionInfo() {
+        super();
+    }
+
+    public SpaceAvailableExtensionInfo(Buffer buffer) {
+        decode(buffer, this);
+    }
+
+    public SpaceAvailableExtensionInfo(FileStore store) throws IOException {
+        bytesOnDevice = store.getTotalSpace();
+
+        long unallocated = store.getUnallocatedSpace();
+        long usable = store.getUsableSpace();
+        unusedBytesOnDevice = Math.max(unallocated, usable);
+
+        // the rest are intentionally  left zero indicating "UNKNOWN"
+    }
+
+    @Override
+    public int hashCode() {
+        return NumberUtils.hashCode(bytesOnDevice, unusedBytesOnDevice,
+                    bytesAvailableToUser, unusedBytesAvailableToUser,
+                    bytesPerAllocationUnit);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        SpaceAvailableExtensionInfo other = (SpaceAvailableExtensionInfo) obj;
+        return this.bytesOnDevice == other.bytesOnDevice
+                && this.unusedBytesOnDevice == other.unusedBytesOnDevice
+                && this.bytesAvailableToUser == other.bytesAvailableToUser
+                && this.unusedBytesAvailableToUser == 
other.unusedBytesAvailableToUser
+                && this.bytesPerAllocationUnit == other.bytesPerAllocationUnit;
+    }
+
+    @Override
+    public SpaceAvailableExtensionInfo clone() {
+        try {
+            return getClass().cast(super.clone());
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException("Failed to close " + toString() + ": " 
+ e.getMessage());
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "bytesOnDevice=" + bytesOnDevice
+                + ",unusedBytesOnDevice=" + unusedBytesOnDevice
+                + ",bytesAvailableToUser=" + bytesAvailableToUser
+                + ",unusedBytesAvailableToUser=" + unusedBytesAvailableToUser
+                + ",bytesPerAllocationUnit=" + bytesPerAllocationUnit;
+    }
+
+    public static SpaceAvailableExtensionInfo decode(Buffer buffer) {
+        SpaceAvailableExtensionInfo info = new SpaceAvailableExtensionInfo();
+        decode(buffer, info);
+        return info;
+    }
+
+    public static void decode(Buffer buffer, SpaceAvailableExtensionInfo info) 
{
+        info.bytesOnDevice = buffer.getLong();
+        info.unusedBytesOnDevice = buffer.getLong();
+        info.bytesAvailableToUser = buffer.getLong();
+        info.unusedBytesAvailableToUser = buffer.getLong();
+        info.bytesPerAllocationUnit = buffer.getInt();
+    }
+
+    public static void encode(Buffer buffer, SpaceAvailableExtensionInfo info) 
{
+        buffer.putLong(info.bytesOnDevice);
+        buffer.putLong(info.unusedBytesOnDevice);
+        buffer.putLong(info.bytesAvailableToUser);
+        buffer.putLong(info.unusedBytesAvailableToUser);
+        buffer.putInt(info.bytesPerAllocationUnit & 0xFFFFFFFFL);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/Supported2Parser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/Supported2Parser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/Supported2Parser.java
new file mode 100644
index 0000000..6259a7c
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/Supported2Parser.java
@@ -0,0 +1,93 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import java.util.Collection;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.Supported2Parser.Supported2;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+/**
+ * Parses the &quot;supported2&quot; extension as defined in
+ * <A 
HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-10";>DRAFT 
13 section 5.4</A>
+ *
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class Supported2Parser extends AbstractParser<Supported2> {
+    /**
+     * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD 
Project</a>
+     * @see <A 
HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-10";>DRAFT 
13 section 5.4</A>
+     */
+    public static class Supported2 {
+        // CHECKSTYLE:OFF
+        public int supportedAttributeMask;
+        public int supportedAttributeBits;
+        public int supportedOpenFlags;
+        public int supportedAccessMask;
+        public int maxReadSize;
+        public short supportedOpenBlockVector;
+        public short supportedBlock;
+        //        uint32 attrib-extension-count
+        public Collection<String> attribExtensionNames;
+        //        uint32 extension-count
+        public Collection<String> extensionNames;
+        // CHECKSTYLE:ON
+
+        @Override
+        public String toString() {
+            return "attrsMask=0x" + Integer.toHexString(supportedAttributeMask)
+                    + ",attrsBits=0x" + 
Integer.toHexString(supportedAttributeBits)
+                    + ",openFlags=0x" + Integer.toHexString(supportedOpenFlags)
+                    + ",accessMask=0x" + 
Integer.toHexString(supportedAccessMask)
+                    + ",maxRead=" + maxReadSize
+                    + ",openBlock=0x" + 
Integer.toHexString(supportedOpenBlockVector & 0xFFFF)
+                    + ",block=" + Integer.toHexString(supportedBlock & 0xFFFF)
+                    + ",attribs=" + attribExtensionNames
+                    + ",exts=" + extensionNames;
+        }
+    }
+
+    public static final Supported2Parser INSTANCE = new Supported2Parser();
+
+    public Supported2Parser() {
+        super(SftpConstants.EXT_SUPPORTED2);
+    }
+
+    @Override
+    public Supported2 parse(byte[] input, int offset, int len) {
+        return parse(new ByteArrayBuffer(input, offset, len));
+    }
+
+    public Supported2 parse(Buffer buffer) {
+        Supported2 sup2 = new Supported2();
+        sup2.supportedAttributeMask = buffer.getInt();
+        sup2.supportedAttributeBits = buffer.getInt();
+        sup2.supportedOpenFlags = buffer.getInt();
+        sup2.supportedAccessMask = buffer.getInt();
+        sup2.maxReadSize = buffer.getInt();
+        sup2.supportedOpenBlockVector = buffer.getShort();
+        sup2.supportedBlock = buffer.getShort();
+        sup2.attribExtensionNames = buffer.getStringList(true);
+        sup2.extensionNames = buffer.getStringList(true);
+        return sup2;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SupportedParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SupportedParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SupportedParser.java
new file mode 100644
index 0000000..4c80463
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SupportedParser.java
@@ -0,0 +1,82 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import java.util.Collection;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.SupportedParser.Supported;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+/**
+ * Parses the &quot;supported&quot; extension as defined in
+ * <A 
HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-05.txt";>DRAFT
 05 - section 4.4</A>
+ *
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class SupportedParser extends AbstractParser<Supported> {
+    /**
+     * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD 
Project</a>
+     * @see <A 
HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-05.txt";>DRAFT
 05 - section 4.4</A>
+     */
+    public static class Supported {
+        // CHECKSTYLE:OFF
+        public int supportedAttributeMask;
+        public int supportedAttributeBits;
+        public int supportedOpenFlags;
+        public int supportedAccessMask;
+        public int maxReadSize;
+        public Collection<String> extensionNames;
+        // CHECKSTYLE:ON
+
+        @Override
+        public String toString() {
+            return "attrsMask=0x" + Integer.toHexString(supportedAttributeMask)
+                    + ",attrsBits=0x" + 
Integer.toHexString(supportedAttributeBits)
+                    + ",openFlags=0x" + Integer.toHexString(supportedOpenFlags)
+                    + ",accessMask=0x" + 
Integer.toHexString(supportedAccessMask)
+                    + ",maxReadSize=" + maxReadSize
+                    + ",extensions=" + extensionNames;
+        }
+    }
+
+    public static final SupportedParser INSTANCE = new SupportedParser();
+
+    public SupportedParser() {
+        super(SftpConstants.EXT_SUPPORTED);
+    }
+
+    @Override
+    public Supported parse(byte[] input, int offset, int len) {
+        return parse(new ByteArrayBuffer(input, offset, len));
+    }
+
+    public Supported parse(Buffer buffer) {
+        Supported sup = new Supported();
+        sup.supportedAttributeMask = buffer.getInt();
+        sup.supportedAttributeBits = buffer.getInt();
+        sup.supportedOpenFlags = buffer.getInt();
+        sup.supportedAccessMask = buffer.getInt();
+        sup.maxReadSize = buffer.getInt();
+        sup.extensionNames = buffer.getStringList(false);
+        return sup;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VendorIdParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VendorIdParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VendorIdParser.java
new file mode 100644
index 0000000..1917d7d
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VendorIdParser.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.VendorIdParser.VendorId;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class VendorIdParser extends AbstractParser<VendorId> {
+    /**
+     * The &quot;vendor-id&quot; information as per
+     * <A 
HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt";>DRAFT
 09 - section 4.4</A>
+     *
+     * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD 
Project</a>
+     */
+    public static class VendorId {
+        // CHECKSTYLE:OFF
+        public String vendorName;
+        public String productName;
+        public String productVersion;
+        public long productBuildNumber;
+        // CHECKSTYLE:ON
+
+        @Override
+        public String toString() {
+            return vendorName + "-" + productName + "-" + productVersion + "-" 
+ productBuildNumber;
+        }
+    }
+
+    public static final VendorIdParser INSTANCE = new VendorIdParser();
+
+    public VendorIdParser() {
+        super(SftpConstants.EXT_VENDOR_ID);
+    }
+
+    @Override
+    public VendorId parse(byte[] input, int offset, int len) {
+        return parse(new ByteArrayBuffer(input, offset, len));
+    }
+
+    public VendorId parse(Buffer buffer) {
+        VendorId id = new VendorId();
+        id.vendorName = buffer.getString();
+        id.productName = buffer.getString();
+        id.productVersion = buffer.getString();
+        id.productBuildNumber = buffer.getLong();
+        return id;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VersionsParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VersionsParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VersionsParser.java
new file mode 100644
index 0000000..51b31f6
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/VersionsParser.java
@@ -0,0 +1,83 @@
+/*
+ * 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.common.subsystem.sftp.extensions;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.VersionsParser.Versions;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class VersionsParser extends AbstractParser<Versions> {
+    /**
+     * The &quot;versions&quot; extension data as per
+     * <A 
HREF="http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt";>DRAFT
 09 Section 4.6</A>
+     *
+     * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD 
Project</a>
+     */
+    public static class Versions {
+        public static final char SEP = ',';
+
+        private List<String> versions;
+
+        public Versions() {
+            this(null);
+        }
+
+        public Versions(List<String> versions) {
+            this.versions = versions;
+        }
+
+        public List<String> getVersions() {
+            return versions;
+        }
+
+        public void setVersions(List<String> versions) {
+            this.versions = versions;
+        }
+
+        @Override
+        public String toString() {
+            return GenericUtils.join(getVersions(), ',');
+        }
+    }
+
+    public static final VersionsParser INSTANCE = new VersionsParser();
+
+    public VersionsParser() {
+        super(SftpConstants.EXT_VERSIONS);
+    }
+
+    @Override
+    public Versions parse(byte[] input, int offset, int len) {
+        return parse(new String(input, offset, len, StandardCharsets.UTF_8));
+    }
+
+    public Versions parse(String value) {
+        String[] comps = GenericUtils.split(value, Versions.SEP);
+        return new Versions(GenericUtils.isEmpty(comps) ? 
Collections.emptyList() : Arrays.asList(comps));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
new file mode 100644
index 0000000..8590e64
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
@@ -0,0 +1,113 @@
+/*
+ * 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.common.subsystem.sftp.extensions.openssh;
+
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.subsystem.sftp.extensions.AbstractParser;
+import 
org.apache.sshd.common.subsystem.sftp.extensions.openssh.AbstractOpenSSHExtensionParser.OpenSSHExtension;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Base class for various {@code x...@openssh.com} extension data reports
+ *
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractOpenSSHExtensionParser extends 
AbstractParser<OpenSSHExtension> {
+    public static class OpenSSHExtension implements NamedResource, Cloneable, 
Serializable {
+        private static final long serialVersionUID = 5902797870154506909L;
+        private final String name;
+        private String version;
+
+        public OpenSSHExtension(String name) {
+            this(name, null);
+        }
+
+        public OpenSSHExtension(String name, String version) {
+            this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No 
extension name");
+            this.version = version;
+        }
+
+        @Override
+        public final String getName() {
+            return name;
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        public void setVersion(String version) {
+            this.version = version;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getName(), getVersion());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (this == obj) {
+                return true;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+
+            OpenSSHExtension other = (OpenSSHExtension) obj;
+            return Objects.equals(getName(), other.getName())
+                    && Objects.equals(getVersion(), other.getVersion());
+        }
+
+        @Override
+        public OpenSSHExtension clone() {
+            try {
+                return getClass().cast(super.clone());
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("Unexpected clone exception " + 
toString() + ": " + e.getMessage());
+            }
+        }
+
+        @Override
+        public String toString() {
+            return getName() + " " + getVersion();
+        }
+    }
+
+    protected AbstractOpenSSHExtensionParser(String name) {
+        super(name);
+    }
+
+    @Override
+    public OpenSSHExtension parse(byte[] input, int offset, int len) {
+        return parse(new String(input, offset, len, StandardCharsets.UTF_8));
+    }
+
+    public OpenSSHExtension parse(String version) {
+        return new OpenSSHExtension(getName(), version);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FstatVfsExtensionParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FstatVfsExtensionParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FstatVfsExtensionParser.java
new file mode 100644
index 0000000..4d13bf4
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FstatVfsExtensionParser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.common.subsystem.sftp.extensions.openssh;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class FstatVfsExtensionParser extends AbstractOpenSSHExtensionParser {
+    public static final String NAME = "fstat...@openssh.com";
+    public static final FstatVfsExtensionParser INSTANCE = new 
FstatVfsExtensionParser();
+
+    public FstatVfsExtensionParser() {
+        super(NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
new file mode 100644
index 0000000..e9967ab
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/FsyncExtensionParser.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.subsystem.sftp.extensions.openssh;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ * @see <A 
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL";>OpenSSH 
-  section 10</A>
+ */
+public class FsyncExtensionParser extends AbstractOpenSSHExtensionParser {
+    public static final String NAME = "fs...@openssh.com";
+    public static final FsyncExtensionParser INSTANCE = new 
FsyncExtensionParser();
+
+    public FsyncExtensionParser() {
+        super(NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/HardLinkExtensionParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/HardLinkExtensionParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/HardLinkExtensionParser.java
new file mode 100644
index 0000000..6d79a78
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/HardLinkExtensionParser.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.subsystem.sftp.extensions.openssh;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ * @see <A 
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL";>OpenSSH 
- section 10</A>
+ */
+public class HardLinkExtensionParser extends AbstractOpenSSHExtensionParser {
+    public static final String NAME = "hardl...@openssh.com";
+    public static final HardLinkExtensionParser INSTANCE = new 
HardLinkExtensionParser();
+
+    public HardLinkExtensionParser() {
+        super(NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/PosixRenameExtensionParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/PosixRenameExtensionParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/PosixRenameExtensionParser.java
new file mode 100644
index 0000000..151c1ee
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/PosixRenameExtensionParser.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.subsystem.sftp.extensions.openssh;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ * @see <A 
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL";>OpenSSH 
- section 3.3</A>
+ */
+public class PosixRenameExtensionParser extends AbstractOpenSSHExtensionParser 
{
+    public static final String NAME = "posix-ren...@openssh.com";
+    public static final PosixRenameExtensionParser INSTANCE = new 
PosixRenameExtensionParser();
+
+    public PosixRenameExtensionParser() {
+        super(NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/StatVfsExtensionParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/StatVfsExtensionParser.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/StatVfsExtensionParser.java
new file mode 100644
index 0000000..be0fd8a
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/StatVfsExtensionParser.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.subsystem.sftp.extensions.openssh;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ * @see <A 
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL";>OpenSSH 
- section 3.4</A>
+ */
+public class StatVfsExtensionParser extends AbstractOpenSSHExtensionParser {
+    public static final String NAME = "stat...@openssh.com";
+    public static final StatVfsExtensionParser INSTANCE = new 
StatVfsExtensionParser();
+
+    public StatVfsExtensionParser() {
+        super(NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerAdapter.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerAdapter.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerAdapter.java
new file mode 100644
index 0000000..6895bae
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerAdapter.java
@@ -0,0 +1,258 @@
+/*
+ * 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.server.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * A no-op implementation of {@link SftpEventListener} for those who wish to
+ * implement only a small number of methods. By default, all non-overridden 
methods
+ * simply log at TRACE level their invocation parameters
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSftpEventListenerAdapter extends 
AbstractLoggingBean implements SftpEventListener {
+    protected AbstractSftpEventListenerAdapter() {
+        super();
+    }
+
+    @Override
+    public void initialized(ServerSession session, int version) {
+        if (log.isTraceEnabled()) {
+            log.trace("initialized(" + session + ") version: " + version);
+        }
+    }
+
+    @Override
+    public void destroying(ServerSession session) {
+        if (log.isTraceEnabled()) {
+            log.trace("destroying(" + session + ")");
+        }
+    }
+
+    @Override
+    public void opening(ServerSession session, String remoteHandle, Handle 
localHandle) throws IOException {
+        if (log.isTraceEnabled()) {
+            Path path = localHandle.getFile();
+            log.trace("opening(" + session + ")[" + remoteHandle + "] " + 
(Files.isDirectory(path) ? "directory" : "file") + " " + path);
+        }
+    }
+
+    @Override
+    public void open(ServerSession session, String remoteHandle, Handle 
localHandle) {
+        if (log.isTraceEnabled()) {
+            Path path = localHandle.getFile();
+            log.trace("open(" + session + ")[" + remoteHandle + "] " + 
(Files.isDirectory(path) ? "directory" : "file") + " " + path);
+        }
+    }
+
+    @Override
+    public void read(ServerSession session, String remoteHandle, 
DirectoryHandle localHandle, Map<String, Path> entries)
+            throws IOException {
+        int numEntries = GenericUtils.size(entries);
+        if (log.isDebugEnabled()) {
+            log.debug("read(" + session + ")[" + localHandle.getFile() + "] " 
+ numEntries + " entries");
+        }
+
+        if ((numEntries > 0) && log.isTraceEnabled()) {
+            entries.forEach((key, value) ->
+                log.trace("read(" + session + ")[" + localHandle.getFile() + 
"] " + key + " - " + value));
+        }
+    }
+
+    @Override
+    public void reading(ServerSession session, String remoteHandle, FileHandle 
localHandle,
+                     long offset, byte[] data, int dataOffset, int dataLen)
+                        throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("reading(" + session + ")[" + localHandle.getFile() + "] 
offset=" + offset + ", requested=" + dataLen);
+        }
+    }
+
+    @Override
+    public void read(ServerSession session, String remoteHandle, FileHandle 
localHandle,
+                     long offset, byte[] data, int dataOffset, int dataLen, 
int readLen, Throwable thrown)
+                        throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("read(" + session + ")[" + localHandle.getFile() + "] 
offset=" + offset
+                    + ", requested=" + dataLen + ", read=" + readLen
+                    + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void writing(ServerSession session, String remoteHandle, FileHandle 
localHandle,
+                      long offset, byte[] data, int dataOffset, int dataLen)
+                              throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("write(" + session + ")[" + localHandle.getFile() + "] 
offset=" + offset + ", requested=" + dataLen);
+        }
+    }
+
+    @Override
+    public void written(ServerSession session, String remoteHandle, FileHandle 
localHandle,
+                      long offset, byte[] data, int dataOffset, int dataLen, 
Throwable thrown)
+                              throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("written(" + session + ")[" + localHandle.getFile() + "] 
offset=" + offset + ", requested=" + dataLen
+                    + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void blocking(ServerSession session, String remoteHandle, 
FileHandle localHandle, long offset, long length, int mask)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("blocking(" + session + ")[" + localHandle.getFile() + 
"]"
+                   + " offset=" + offset + ", length=" + length + ", mask=0x" 
+ Integer.toHexString(mask));
+        }
+    }
+
+    @Override
+    public void blocked(ServerSession session, String remoteHandle, FileHandle 
localHandle,
+                        long offset, long length, int mask, Throwable thrown)
+                                throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("blocked(" + session + ")[" + localHandle.getFile() + "]"
+                    + " offset=" + offset + ", length=" + length + ", mask=0x" 
+ Integer.toHexString(mask)
+                    + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void unblocking(ServerSession session, String remoteHandle, 
FileHandle localHandle, long offset, long length)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("unblocking(" + session + ")[" + localHandle.getFile() + 
"] offset=" + offset + ", length=" + length);
+        }
+    }
+
+    @Override
+    public void unblocked(ServerSession session, String remoteHandle, 
FileHandle localHandle,
+                          long offset, long length, Throwable thrown)
+                                  throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("unblocked(" + session + ")[" + localHandle.getFile() + 
"]"
+                    + " offset=" + offset + ", length=" + length
+                    + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void close(ServerSession session, String remoteHandle, Handle 
localHandle) {
+        if (log.isTraceEnabled()) {
+            Path path = localHandle.getFile();
+            log.trace("close(" + session + ")[" + remoteHandle + "] " + 
(Files.isDirectory(path) ? "directory" : "file") + " " + path);
+        }
+    }
+
+    @Override
+    public void creating(ServerSession session, Path path, Map<String, ?> 
attrs)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("creating(" + session + ") " + (Files.isDirectory(path) 
? "directory" : "file") + " " + path);
+        }
+    }
+
+    @Override
+    public void created(ServerSession session, Path path, Map<String, ?> 
attrs, Throwable thrown)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("created(" + session + ") " + (Files.isDirectory(path) ? 
"directory" : "file") + " " + path
+                   + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void moving(ServerSession session, Path srcPath, Path dstPath, 
Collection<CopyOption> opts)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("moving(" + session + ")[" + opts + "]" + srcPath + " => 
" + dstPath);
+        }
+    }
+
+    @Override
+    public void moved(ServerSession session, Path srcPath, Path dstPath, 
Collection<CopyOption> opts, Throwable thrown)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("moved(" + session + ")[" + opts + "]" + srcPath + " => 
" + dstPath
+                    + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void removing(ServerSession session, Path path)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("removing(" + session + ") " + path);
+        }
+    }
+
+    @Override
+    public void removed(ServerSession session, Path path, Throwable thrown)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("removed(" + session + ") " + path
+                  + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void linking(ServerSession session, Path source, Path target, 
boolean symLink)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("linking(" + session + ")[" + symLink + "]" + source + " 
=> " + target);
+        }
+    }
+
+    @Override
+    public void linked(ServerSession session, Path source, Path target, 
boolean symLink, Throwable thrown)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("linked(" + session + ")[" + symLink + "]" + source + " 
=> " + target
+                    + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+
+    @Override
+    public void modifyingAttributes(ServerSession session, Path path, 
Map<String, ?> attrs)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("modifyingAttributes(" + session + ") " + path + ": " + 
attrs);
+        }
+    }
+
+    @Override
+    public void modifiedAttributes(ServerSession session, Path path, 
Map<String, ?> attrs, Throwable thrown)
+            throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("modifiedAttributes(" + session + ") " + path
+                  + ((thrown == null) ? "" : (": " + 
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerManager.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerManager.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerManager.java
new file mode 100644
index 0000000..11508b3
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/AbstractSftpEventListenerManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.server.subsystem.sftp;
+
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.sshd.common.util.EventListenerUtils;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSftpEventListenerManager implements 
SftpEventListenerManager {
+    private final Collection<SftpEventListener> sftpEventListeners = new 
CopyOnWriteArraySet<>();
+    private final SftpEventListener sftpEventListenerProxy;
+
+    protected AbstractSftpEventListenerManager() {
+        sftpEventListenerProxy = 
EventListenerUtils.proxyWrapper(SftpEventListener.class, 
getClass().getClassLoader(), sftpEventListeners);
+    }
+
+    public Collection<SftpEventListener> getRegisteredListeners() {
+        return sftpEventListeners;
+    }
+
+    @Override
+    public SftpEventListener getSftpEventListenerProxy() {
+        return sftpEventListenerProxy;
+    }
+
+    @Override
+    public boolean addSftpEventListener(SftpEventListener listener) {
+        return 
sftpEventListeners.add(SftpEventListener.validateListener(listener));
+    }
+
+    @Override
+    public boolean removeSftpEventListener(SftpEventListener listener) {
+        if (listener == null) {
+            return false;
+        }
+
+        return 
sftpEventListeners.remove(SftpEventListener.validateListener(listener));
+    }
+}

Reply via email to