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 2018/04/16 11:48:00 UTC
[13/30] mina-sshd git commit: [SSHD-815] Extract SFTP in its own
module
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpIterableDirEntry.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpIterableDirEntry.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpIterableDirEntry.java
new file mode 100644
index 0000000..945e0d7
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpIterableDirEntry.java
@@ -0,0 +1,72 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provides an {@link Iterable} implementation of the {@link DirEntry}-ies
+ * for a remote directory
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpIterableDirEntry implements Iterable<DirEntry> {
+ private final SftpClient client;
+ private final String path;
+
+ /**
+ * @param client The {@link SftpClient} instance to use for the iteration
+ * @param path The remote directory path
+ */
+ public SftpIterableDirEntry(SftpClient client, String path) {
+ this.client = Objects.requireNonNull(client, "No client instance");
+ this.path = ValidateUtils.checkNotNullAndNotEmpty(path, "No remote path");
+ }
+
+ /**
+ * The client instance
+ *
+ * @return {@link SftpClient} instance used to access the remote file
+ */
+ public final SftpClient getClient() {
+ return client;
+ }
+
+ /**
+ * The remotely accessed directory path
+ *
+ * @return Remote directory path
+ */
+ public final String getPath() {
+ return path;
+ }
+
+ @Override
+ public SftpDirEntryIterator iterator() {
+ try {
+ return new SftpDirEntryIterator(getClient(), getPath());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ 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/client/subsystem/sftp/SftpOutputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpOutputStreamWithChannel.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpOutputStreamWithChannel.java
new file mode 100644
index 0000000..cf6d972
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpOutputStreamWithChannel.java
@@ -0,0 +1,124 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.OpenMode;
+import org.apache.sshd.common.util.io.OutputStreamWithChannel;
+
+/**
+ * Implements an output stream for a given remote file
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpOutputStreamWithChannel extends OutputStreamWithChannel {
+ private final SftpClient client;
+ private final String path;
+ private final byte[] bb = new byte[1];
+ private final byte[] buffer;
+ private int index;
+ private CloseableHandle handle;
+ private long offset;
+
+ public SftpOutputStreamWithChannel(SftpClient client, int bufferSize, String path, Collection<OpenMode> mode) throws IOException {
+ this.client = Objects.requireNonNull(client, "No SFTP client instance");
+ this.path = path;
+ buffer = new byte[bufferSize];
+ handle = client.open(path, mode);
+ }
+
+ /**
+ * The client instance
+ *
+ * @return {@link SftpClient} instance used to access the remote file
+ */
+ public final SftpClient getClient() {
+ return client;
+ }
+
+ /**
+ * The remotely accessed file path
+ *
+ * @return Remote file path
+ */
+ public final String getPath() {
+ return path;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return (handle != null) && handle.isOpen();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ bb[0] = (byte) b;
+ write(bb, 0, 1);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (!isOpen()) {
+ throw new IOException("write(" + getPath() + ")[len=" + len + "] stream is closed");
+ }
+
+ do {
+ int nb = Math.min(len, buffer.length - index);
+ System.arraycopy(b, off, buffer, index, nb);
+ index += nb;
+ if (index == buffer.length) {
+ flush();
+ }
+ off += nb;
+ len -= nb;
+ } while (len > 0);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (!isOpen()) {
+ throw new IOException("flush(" + getPath() + ") stream is closed");
+ }
+
+ client.write(handle, offset, buffer, 0, index);
+ offset += index;
+ index = 0;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (isOpen()) {
+ try {
+ try {
+ if (index > 0) {
+ flush();
+ }
+ } finally {
+ handle.close();
+ }
+ } finally {
+ handle = null;
+ }
+ }
+ }
+}
\ 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/client/subsystem/sftp/SftpPath.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.java
new file mode 100644
index 0000000..5567b58
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPath.java
@@ -0,0 +1,43 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.LinkOption;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.List;
+
+import org.apache.sshd.common.file.util.BasePath;
+
+public class SftpPath extends BasePath<SftpPath, SftpFileSystem> {
+ public SftpPath(SftpFileSystem fileSystem, String root, List<String> names) {
+ super(fileSystem, root, names);
+ }
+
+ @Override
+ public SftpPath toRealPath(LinkOption... options) throws IOException {
+ // TODO: handle links
+ SftpPath absolute = toAbsolutePath();
+ FileSystem fs = getFileSystem();
+ FileSystemProvider provider = fs.provider();
+ provider.checkAccess(absolute);
+ return absolute;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.java
new file mode 100644
index 0000000..49b4b48
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPathIterator.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.client.subsystem.sftp;
+
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpPathIterator implements Iterator<Path> {
+ private final SftpPath p;
+ private final Iterator<? extends SftpClient.DirEntry> it;
+ private boolean dotIgnored;
+ private boolean dotdotIgnored;
+ private SftpClient.DirEntry curEntry;
+
+ public SftpPathIterator(SftpPath path, Iterable<? extends SftpClient.DirEntry> iter) {
+ this(path, (iter == null) ? null : iter.iterator());
+ }
+
+ public SftpPathIterator(SftpPath path, Iterator<? extends SftpClient.DirEntry> iter) {
+ p = path;
+ it = iter;
+ curEntry = nextEntry();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return curEntry != null;
+ }
+
+ @Override
+ public Path next() {
+ if (curEntry == null) {
+ throw new NoSuchElementException("No next entry");
+ }
+
+ SftpClient.DirEntry entry = curEntry;
+ curEntry = nextEntry();
+ return p.resolve(entry.getFilename());
+ }
+
+ private SftpClient.DirEntry nextEntry() {
+ while ((it != null) && it.hasNext()) {
+ SftpClient.DirEntry entry = it.next();
+ String name = entry.getFilename();
+ if (".".equals(name) && (!dotIgnored)) {
+ dotIgnored = true;
+ } else if ("..".equals(name) && (!dotdotIgnored)) {
+ dotdotIgnored = true;
+ } else {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("newDirectoryStream(" + p + ") Iterator#remove() N/A");
+ }
+}
\ 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/client/subsystem/sftp/SftpPosixFileAttributeView.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java
new file mode 100644
index 0000000..1fb614c
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributeView.java
@@ -0,0 +1,94 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.Set;
+
+import org.apache.sshd.client.subsystem.sftp.impl.AbstractSftpFileAttributeView;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpPosixFileAttributeView extends AbstractSftpFileAttributeView implements PosixFileAttributeView {
+ public SftpPosixFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) {
+ super(provider, path, options);
+ }
+
+ @Override
+ public String name() {
+ return "posix";
+ }
+
+ @Override
+ public PosixFileAttributes readAttributes() throws IOException {
+ return new SftpPosixFileAttributes(path, readRemoteAttributes());
+ }
+
+ @Override
+ public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException {
+ SftpClient.Attributes attrs = new SftpClient.Attributes();
+ if (lastModifiedTime != null) {
+ attrs.modifyTime(lastModifiedTime);
+ }
+ if (lastAccessTime != null) {
+ attrs.accessTime(lastAccessTime);
+ }
+ if (createTime != null) {
+ attrs.createTime(createTime);
+ }
+
+ if (GenericUtils.isEmpty(attrs.getFlags())) {
+ if (log.isDebugEnabled()) {
+ log.debug("setTimes({}) no changes", path);
+ }
+ } else {
+ writeRemoteAttributes(attrs);
+ }
+ }
+
+ @Override
+ public void setPermissions(Set<PosixFilePermission> perms) throws IOException {
+ provider.setAttribute(path, "permissions", perms, options);
+ }
+
+ @Override
+ public void setGroup(GroupPrincipal group) throws IOException {
+ provider.setAttribute(path, "group", group, options);
+ }
+
+ @Override
+ public UserPrincipal getOwner() throws IOException {
+ return readAttributes().owner();
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner) throws IOException {
+ provider.setAttribute(path, "owner", owner, options);
+ }
+}
\ 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/client/subsystem/sftp/SftpPosixFileAttributes.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.java
new file mode 100644
index 0000000..a07e67f
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpPosixFileAttributes.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.client.subsystem.sftp;
+
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.Set;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpPosixFileAttributes implements PosixFileAttributes {
+ private final Path path;
+ private final Attributes attributes;
+
+ public SftpPosixFileAttributes(Path path, Attributes attributes) {
+ this.path = path;
+ this.attributes = attributes;
+ }
+
+ /**
+ * @return The referenced attributes file {@link Path}
+ */
+ public final Path getPath() {
+ return path;
+ }
+
+ @Override
+ public UserPrincipal owner() {
+ String owner = attributes.getOwner();
+ return GenericUtils.isEmpty(owner) ? null : new SftpFileSystem.DefaultUserPrincipal(owner);
+ }
+
+ @Override
+ public GroupPrincipal group() {
+ String group = attributes.getGroup();
+ return GenericUtils.isEmpty(group) ? null : new SftpFileSystem.DefaultGroupPrincipal(group);
+ }
+
+ @Override
+ public Set<PosixFilePermission> permissions() {
+ return SftpFileSystemProvider.permissionsToAttributes(attributes.getPermissions());
+ }
+
+ @Override
+ public FileTime lastModifiedTime() {
+ return attributes.getModifyTime();
+ }
+
+ @Override
+ public FileTime lastAccessTime() {
+ return attributes.getAccessTime();
+ }
+
+ @Override
+ public FileTime creationTime() {
+ return attributes.getCreateTime();
+ }
+
+ @Override
+ public boolean isRegularFile() {
+ return attributes.isRegularFile();
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return attributes.isDirectory();
+ }
+
+ @Override
+ public boolean isSymbolicLink() {
+ return attributes.isSymbolicLink();
+ }
+
+ @Override
+ public boolean isOther() {
+ return attributes.isOther();
+ }
+
+ @Override
+ public long size() {
+ return attributes.getSize();
+ }
+
+ @Override
+ public Object fileKey() {
+ // TODO consider implementing this
+ return null;
+ }
+}
\ 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/client/subsystem/sftp/SftpRemotePathChannel.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java
new file mode 100644
index 0000000..9195009
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java
@@ -0,0 +1,412 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.subsystem.sftp.SftpException;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpRemotePathChannel extends FileChannel {
+ public static final String COPY_BUFSIZE_PROP = "sftp-channel-copy-buf-size";
+ public static final int DEFAULT_TRANSFER_BUFFER_SIZE = IoUtils.DEFAULT_COPY_SIZE;
+
+ public static final Set<SftpClient.OpenMode> READ_MODES =
+ Collections.unmodifiableSet(EnumSet.of(SftpClient.OpenMode.Read));
+
+ public static final Set<SftpClient.OpenMode> WRITE_MODES =
+ Collections.unmodifiableSet(
+ EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Append, SftpClient.OpenMode.Create, SftpClient.OpenMode.Truncate));
+
+ private final String path;
+ private final Collection<SftpClient.OpenMode> modes;
+ private final boolean closeOnExit;
+ private final SftpClient sftp;
+ private final SftpClient.CloseableHandle handle;
+ private final Object lock = new Object();
+ private final AtomicLong posTracker = new AtomicLong(0L);
+ private final AtomicReference<Thread> blockingThreadHolder = new AtomicReference<>(null);
+
+ public SftpRemotePathChannel(String path, SftpClient sftp, boolean closeOnExit, Collection<SftpClient.OpenMode> modes) throws IOException {
+ this.path = ValidateUtils.checkNotNullAndNotEmpty(path, "No remote file path specified");
+ this.modes = Objects.requireNonNull(modes, "No channel modes specified");
+ this.sftp = Objects.requireNonNull(sftp, "No SFTP client instance");
+ this.closeOnExit = closeOnExit;
+ this.handle = sftp.open(path, modes);
+ }
+
+ public String getRemotePath() {
+ return path;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ return (int) doRead(Collections.singletonList(dst), -1);
+ }
+
+ @Override
+ public int read(ByteBuffer dst, long position) throws IOException {
+ if (position < 0) {
+ throw new IllegalArgumentException("read(" + getRemotePath() + ") illegal position to read from: " + position);
+ }
+ return (int) doRead(Collections.singletonList(dst), position);
+ }
+
+ @Override
+ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
+ List<ByteBuffer> buffers = Arrays.asList(dsts).subList(offset, offset + length);
+ return doRead(buffers, -1);
+ }
+
+ protected long doRead(List<ByteBuffer> buffers, long position) throws IOException {
+ ensureOpen(READ_MODES);
+ synchronized (lock) {
+ boolean completed = false;
+ boolean eof = false;
+ long curPos = (position >= 0L) ? position : posTracker.get();
+ try {
+ long totalRead = 0;
+ beginBlocking();
+ loop:
+ for (ByteBuffer buffer : buffers) {
+ while (buffer.remaining() > 0) {
+ ByteBuffer wrap = buffer;
+ if (!buffer.hasArray()) {
+ wrap = ByteBuffer.allocate(Math.min(IoUtils.DEFAULT_COPY_SIZE, buffer.remaining()));
+ }
+ int read = sftp.read(handle, curPos, wrap.array(), wrap.arrayOffset() + wrap.position(), wrap.remaining());
+ if (read > 0) {
+ if (wrap == buffer) {
+ wrap.position(wrap.position() + read);
+ } else {
+ buffer.put(wrap.array(), wrap.arrayOffset(), read);
+ }
+ curPos += read;
+ totalRead += read;
+ } else {
+ eof = read == -1;
+ break loop;
+ }
+ }
+ }
+ completed = true;
+ if (totalRead > 0) {
+ return totalRead;
+ }
+
+ if (eof) {
+ return -1;
+ } else {
+ return 0;
+ }
+ } finally {
+ if (position < 0L) {
+ posTracker.set(curPos);
+ }
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ return (int) doWrite(Collections.singletonList(src), -1);
+ }
+
+ @Override
+ public int write(ByteBuffer src, long position) throws IOException {
+ if (position < 0L) {
+ throw new IllegalArgumentException("write(" + getRemotePath() + ") illegal position to write to: " + position);
+ }
+ return (int) doWrite(Collections.singletonList(src), position);
+ }
+
+ @Override
+ public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
+ List<ByteBuffer> buffers = Arrays.asList(srcs).subList(offset, offset + length);
+ return doWrite(buffers, -1);
+ }
+
+ protected long doWrite(List<ByteBuffer> buffers, long position) throws IOException {
+ ensureOpen(WRITE_MODES);
+ synchronized (lock) {
+ boolean completed = false;
+ long curPos = (position >= 0L) ? position : posTracker.get();
+ try {
+ long totalWritten = 0L;
+ beginBlocking();
+ for (ByteBuffer buffer : buffers) {
+ while (buffer.remaining() > 0) {
+ ByteBuffer wrap = buffer;
+ if (!buffer.hasArray()) {
+ wrap = ByteBuffer.allocate(Math.min(IoUtils.DEFAULT_COPY_SIZE, buffer.remaining()));
+ buffer.get(wrap.array(), wrap.arrayOffset(), wrap.remaining());
+ }
+ int written = wrap.remaining();
+ sftp.write(handle, curPos, wrap.array(), wrap.arrayOffset() + wrap.position(), written);
+ if (wrap == buffer) {
+ wrap.position(wrap.position() + written);
+ }
+ curPos += written;
+ totalWritten += written;
+ }
+ }
+ completed = true;
+ return totalWritten;
+ } finally {
+ if (position < 0L) {
+ posTracker.set(curPos);
+ }
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public long position() throws IOException {
+ ensureOpen(Collections.emptySet());
+ return posTracker.get();
+ }
+
+ @Override
+ public FileChannel position(long newPosition) throws IOException {
+ if (newPosition < 0L) {
+ throw new IllegalArgumentException("position(" + getRemotePath() + ") illegal file channel position: " + newPosition);
+ }
+
+ ensureOpen(Collections.emptySet());
+ posTracker.set(newPosition);
+ return this;
+ }
+
+ @Override
+ public long size() throws IOException {
+ ensureOpen(Collections.emptySet());
+ return sftp.stat(handle).getSize();
+ }
+
+ @Override
+ public FileChannel truncate(long size) throws IOException {
+ ensureOpen(Collections.emptySet());
+ sftp.setStat(handle, new SftpClient.Attributes().size(size));
+ return this;
+ }
+
+ @Override
+ public void force(boolean metaData) throws IOException {
+ ensureOpen(Collections.emptySet());
+ }
+
+ @Override
+ public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
+ if ((position < 0) || (count < 0)) {
+ throw new IllegalArgumentException("transferTo(" + getRemotePath() + ") illegal position (" + position + ") or count (" + count + ")");
+ }
+ ensureOpen(READ_MODES);
+ synchronized (lock) {
+ boolean completed = false;
+ boolean eof = false;
+ long curPos = position;
+ try {
+ beginBlocking();
+
+ int bufSize = (int) Math.min(count, Short.MAX_VALUE + 1);
+ byte[] buffer = new byte[bufSize];
+ long totalRead = 0L;
+ while (totalRead < count) {
+ int read = sftp.read(handle, curPos, buffer, 0, buffer.length);
+ if (read > 0) {
+ ByteBuffer wrap = ByteBuffer.wrap(buffer);
+ while (wrap.remaining() > 0) {
+ target.write(wrap);
+ }
+ curPos += read;
+ totalRead += read;
+ } else {
+ eof = read == -1;
+ }
+ }
+ completed = true;
+ return totalRead > 0 ? totalRead : eof ? -1 : 0;
+ } finally {
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
+ if ((position < 0) || (count < 0)) {
+ throw new IllegalArgumentException("transferFrom(" + getRemotePath() + ") illegal position (" + position + ") or count (" + count + ")");
+ }
+ ensureOpen(WRITE_MODES);
+
+ int copySize = sftp.getClientSession().getIntProperty(COPY_BUFSIZE_PROP, DEFAULT_TRANSFER_BUFFER_SIZE);
+ boolean completed = false;
+ long curPos = (position >= 0L) ? position : posTracker.get();
+ long totalRead = 0L;
+ byte[] buffer = new byte[(int) Math.min(copySize, count)];
+
+ synchronized (lock) {
+ try {
+ beginBlocking();
+
+ while (totalRead < count) {
+ ByteBuffer wrap = ByteBuffer.wrap(buffer, 0, (int) Math.min(buffer.length, count - totalRead));
+ int read = src.read(wrap);
+ if (read > 0) {
+ sftp.write(handle, curPos, buffer, 0, read);
+ curPos += read;
+ totalRead += read;
+ } else {
+ break;
+ }
+ }
+ completed = true;
+ return totalRead;
+ } finally {
+ endBlocking(completed);
+ }
+ }
+ }
+
+ @Override
+ public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
+ throw new UnsupportedOperationException("map(" + getRemotePath() + ")[" + mode + "," + position + "," + size + "] N/A");
+ }
+
+ @Override
+ public FileLock lock(long position, long size, boolean shared) throws IOException {
+ return tryLock(position, size, shared);
+ }
+
+ @Override
+ public FileLock tryLock(final long position, final long size, boolean shared) throws IOException {
+ ensureOpen(Collections.emptySet());
+
+ try {
+ sftp.lock(handle, position, size, 0);
+ } catch (SftpException e) {
+ if (e.getStatus() == SftpConstants.SSH_FX_LOCK_CONFLICT) {
+ throw new OverlappingFileLockException();
+ }
+ throw e;
+ }
+
+ return new FileLock(this, position, size, shared) {
+ private final AtomicBoolean valid = new AtomicBoolean(true);
+
+ @Override
+ public boolean isValid() {
+ return acquiredBy().isOpen() && valid.get();
+ }
+
+ @SuppressWarnings("synthetic-access")
+ @Override
+ public void release() throws IOException {
+ if (valid.compareAndSet(true, false)) {
+ sftp.unlock(handle, position, size);
+ }
+ }
+ };
+ }
+
+ @Override
+ protected void implCloseChannel() throws IOException {
+ try {
+ final Thread thread = blockingThreadHolder.get();
+ if (thread != null) {
+ thread.interrupt();
+ }
+ } finally {
+ try {
+ handle.close();
+ } finally {
+ if (closeOnExit) {
+ sftp.close();
+ }
+ }
+ }
+ }
+
+ private void beginBlocking() {
+ begin();
+ blockingThreadHolder.set(Thread.currentThread());
+ }
+
+ private void endBlocking(boolean completed) throws AsynchronousCloseException {
+ blockingThreadHolder.set(null);
+ end(completed);
+ }
+
+ /**
+ * Checks that the channel is open and that its current mode contains
+ * at least one of the required ones
+ *
+ * @param reqModes The required modes - ignored if {@code null}/empty
+ * @throws IOException If channel not open or the required modes are not
+ * satisfied
+ */
+ private void ensureOpen(Collection<SftpClient.OpenMode> reqModes) throws IOException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+
+ if (GenericUtils.size(reqModes) > 0) {
+ for (SftpClient.OpenMode m : reqModes) {
+ if (this.modes.contains(m)) {
+ return;
+ }
+ }
+
+ throw new IOException("ensureOpen(" + getRemotePath() + ") current channel modes (" + this.modes + ") do contain any of the required: " + reqModes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getRemotePath();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
new file mode 100644
index 0000000..3c58da3
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
@@ -0,0 +1,126 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.StreamSupport;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface SftpVersionSelector {
+ /**
+ * An {@link SftpVersionSelector} that returns the current version
+ */
+ SftpVersionSelector CURRENT = new NamedVersionSelector("CURRENT", (session, current, available) -> current);
+
+ /**
+ * An {@link SftpVersionSelector} that returns the maximum available version
+ */
+ SftpVersionSelector MAXIMUM = new NamedVersionSelector("MAXIMUM", (session, current, available) ->
+ GenericUtils.stream(available).mapToInt(Integer::intValue).max().orElse(current));
+
+ /**
+ * An {@link SftpVersionSelector} that returns the maximum available version
+ */
+ SftpVersionSelector MINIMUM = new NamedVersionSelector("MINIMUM", (session, current, available) ->
+ GenericUtils.stream(available).mapToInt(Integer::intValue).min().orElse(current));
+
+ /**
+ * @param session The {@link ClientSession} through which the SFTP connection is made
+ * @param current The current version negotiated with the server
+ * @param available Extra versions available - may be empty and/or contain only the current one
+ * @return The new requested version - if same as current, then nothing is done
+ */
+ int selectVersion(ClientSession session, int current, List<Integer> available);
+
+ /**
+ * Creates a selector the always returns the requested (fixed version) regardless
+ * of what the current or reported available versions are. If the requested version
+ * is not reported as available then an exception will be eventually thrown by the
+ * client during re-negotiation phase.
+ *
+ * @param version The requested version
+ * @return The {@link SftpVersionSelector}
+ */
+ static SftpVersionSelector fixedVersionSelector(int version) {
+ return new NamedVersionSelector(Integer.toString(version), (session, current, available) -> version);
+ }
+
+ /**
+ * Selects a version in order of preference - if none of the preferred
+ * versions is listed as available then an exception is thrown when the
+ * {@link SftpVersionSelector#selectVersion(ClientSession, int, List)} method is invoked
+ *
+ * @param preferred The preferred versions in decreasing order of
+ * preference (i.e., most preferred is 1st) - may not be {@code null}/empty
+ * @return A {@link SftpVersionSelector} that attempts to select
+ * the most preferred version that is also listed as available.
+ */
+ static SftpVersionSelector preferredVersionSelector(int... preferred) {
+ return preferredVersionSelector(NumberUtils.asList(preferred));
+ }
+
+ /**
+ * Selects a version in order of preference - if none of the preferred
+ * versions is listed as available then an exception is thrown when the
+ * {@link SftpVersionSelector#selectVersion(ClientSession, int, List)} method is invoked
+ *
+ * @param preferred The preferred versions in decreasing order of
+ * preference (i.e., most preferred is 1st)
+ * @return A {@link SftpVersionSelector} that attempts to select
+ * the most preferred version that is also listed as available.
+ */
+ static SftpVersionSelector preferredVersionSelector(Iterable<? extends Number> preferred) {
+ ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) preferred, "Empty preferred versions");
+ return new NamedVersionSelector(GenericUtils.join(preferred, ','), (session, current, available) -> StreamSupport.stream(preferred.spliterator(), false)
+ .mapToInt(Number::intValue)
+ .filter(v -> v == current || available.contains(v))
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("Preferred versions (" + preferred + ") not available: " + available)));
+ }
+
+ class NamedVersionSelector implements SftpVersionSelector {
+ private final String name;
+ private final SftpVersionSelector selector;
+
+ public NamedVersionSelector(String name, SftpVersionSelector selector) {
+ this.name = name;
+ this.selector = selector;
+ }
+
+ @Override
+ public int selectVersion(ClientSession session, int current, List<Integer> available) {
+ return selector.selectVersion(session, current, available);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/StfpIterableDirHandle.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/StfpIterableDirHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/StfpIterableDirHandle.java
new file mode 100644
index 0000000..c3be157
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/StfpIterableDirHandle.java
@@ -0,0 +1,59 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.util.Objects;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
+
+public class StfpIterableDirHandle implements Iterable<DirEntry> {
+ private final SftpClient client;
+ private final Handle handle;
+
+ /**
+ * @param client The {@link SftpClient} to use for iteration
+ * @param handle The remote directory {@link Handle}
+ */
+ public StfpIterableDirHandle(SftpClient client, Handle handle) {
+ this.client = Objects.requireNonNull(client, "No client instance");
+ this.handle = handle;
+ }
+
+ /**
+ * The client instance
+ *
+ * @return {@link SftpClient} instance used to access the remote file
+ */
+ public final SftpClient getClient() {
+ return client;
+ }
+
+ /**
+ * @return The remote directory {@link Handle}
+ */
+ public final Handle getHandle() {
+ return handle;
+ }
+
+ @Override
+ public SftpDirEntryIterator iterator() {
+ return new SftpDirEntryIterator(getClient(), getHandle());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensions.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensions.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensions.java
new file mode 100644
index 0000000..9e83837
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensions.java
@@ -0,0 +1,162 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.client.subsystem.sftp.RawSftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.CheckFileHandleExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.CheckFileNameExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.CopyDataExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.CopyFileExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.MD5FileExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.MD5HandleExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.helpers.SpaceAvailableExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHFsyncExtension;
+import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHStatHandleExtension;
+import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHStatPathExtension;
+import org.apache.sshd.client.subsystem.sftp.extensions.openssh.helpers.OpenSSHFsyncExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.openssh.helpers.OpenSSHStatHandleExtensionImpl;
+import org.apache.sshd.client.subsystem.sftp.extensions.openssh.helpers.OpenSSHStatPathExtensionImpl;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.subsystem.sftp.extensions.ParserUtils;
+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.StatVfsExtensionParser;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinSftpClientExtensions implements SftpClientExtensionFactory {
+ COPY_FILE(SftpConstants.EXT_COPY_FILE, CopyFileExtension.class) {
+ @Override // co-variant return
+ public CopyFileExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new CopyFileExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ COPY_DATA(SftpConstants.EXT_COPY_DATA, CopyDataExtension.class) {
+ @Override // co-variant return
+ public CopyDataExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new CopyDataExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ MD5_FILE(SftpConstants.EXT_MD5_HASH, MD5FileExtension.class) {
+ @Override // co-variant return
+ public MD5FileExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new MD5FileExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ MD5_HANDLE(SftpConstants.EXT_MD5_HASH_HANDLE, MD5HandleExtension.class) {
+ @Override // co-variant return
+ public MD5HandleExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new MD5HandleExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ CHECK_FILE_NAME(SftpConstants.EXT_CHECK_FILE_NAME, CheckFileNameExtension.class) {
+ @Override // co-variant return
+ public CheckFileNameExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new CheckFileNameExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ CHECK_FILE_HANDLE(SftpConstants.EXT_CHECK_FILE_HANDLE, CheckFileHandleExtension.class) {
+ @Override // co-variant return
+ public CheckFileHandleExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new CheckFileHandleExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ SPACE_AVAILABLE(SftpConstants.EXT_SPACE_AVAILABLE, SpaceAvailableExtension.class) {
+ @Override // co-variant return
+ public SpaceAvailableExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new SpaceAvailableExtensionImpl(client, raw, ParserUtils.supportedExtensions(parsed));
+ }
+ },
+ OPENSSH_FSYNC(FsyncExtensionParser.NAME, OpenSSHFsyncExtension.class) {
+ @Override // co-variant return
+ public OpenSSHFsyncExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new OpenSSHFsyncExtensionImpl(client, raw, extensions);
+ }
+ },
+ OPENSSH_STAT_HANDLE(FstatVfsExtensionParser.NAME, OpenSSHStatHandleExtension.class) {
+ @Override // co-variant return
+ public OpenSSHStatHandleExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new OpenSSHStatHandleExtensionImpl(client, raw, extensions);
+ }
+ },
+ OPENSSH_STAT_PATH(StatVfsExtensionParser.NAME, OpenSSHStatPathExtension.class) {
+ @Override // co-variant return
+ public OpenSSHStatPathExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed) {
+ return new OpenSSHStatPathExtensionImpl(client, raw, extensions);
+ }
+ };
+
+ public static final Set<BuiltinSftpClientExtensions> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinSftpClientExtensions.class));
+
+ private final String name;
+
+ private final Class<? extends SftpClientExtension> type;
+
+ BuiltinSftpClientExtensions(String name, Class<? extends SftpClientExtension> type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ public final Class<? extends SftpClientExtension> getType() {
+ return type;
+ }
+
+ public static BuiltinSftpClientExtensions fromName(String n) {
+ return NamedResource.findByName(n, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ public static BuiltinSftpClientExtensions fromInstance(Object o) {
+ return fromType((o == null) ? null : o.getClass());
+ }
+
+ public static BuiltinSftpClientExtensions fromType(Class<?> type) {
+ if ((type == null) || (!SftpClientExtension.class.isAssignableFrom(type))) {
+ return null;
+ }
+
+ // the base class is assignable to everybody so we cannot distinguish between the enum(s)
+ if (SftpClientExtension.class == type) {
+ return null;
+ }
+
+ for (BuiltinSftpClientExtensions v : VALUES) {
+ Class<?> vt = v.getType();
+ if (vt.isAssignableFrom(type)) {
+ return v;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileHandleExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileHandleExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileHandleExtension.java
new file mode 100644
index 0000000..3261a63
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileHandleExtension.java
@@ -0,0 +1,45 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
+
+/**
+ * @author <a href="mailto:dev@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.1.2</A>
+ */
+public interface CheckFileHandleExtension extends SftpClientExtension {
+ /**
+ * @param handle Remote file {@link Handle} - must be a file and opened for read
+ * @param algorithms Hash algorithms in preferred order
+ * @param startOffset Start offset of the hash
+ * @param length Length of data to hash - if zero then till EOF
+ * @param blockSize Input block size to calculate individual hashes - if
+ * zero the <U>one</U> hash of <U>all</U> the data
+ * @return An <U>immutable</U> {@link java.util.Map.Entry} where key=hash algorithm name,
+ * value=the calculated hashes.
+ * @throws IOException If failed to execute the command
+ */
+ Map.Entry<String, Collection<byte[]>> checkFileHandle(Handle handle, Collection<String> algorithms, long startOffset, long length, int blockSize) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileNameExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileNameExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileNameExtension.java
new file mode 100644
index 0000000..14e0204
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CheckFileNameExtension.java
@@ -0,0 +1,43 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:dev@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.1.2</A>
+ */
+public interface CheckFileNameExtension extends SftpClientExtension {
+ /**
+ * @param name Remote file name/path
+ * @param algorithms Hash algorithms in preferred order
+ * @param startOffset Start offset of the hash
+ * @param length Length of data to hash - if zero then till EOF
+ * @param blockSize Input block size to calculate individual hashes - if
+ * zero the <U>one</U> hash of <U>all</U> the data
+ * @return An <U>immutable</U> {@link java.util.Map.Entry} key left=hash algorithm name,
+ * value=the calculated hashes.
+ * @throws IOException If failed to execute the command
+ */
+ Map.Entry<String, Collection<byte[]>> checkFileName(String name, Collection<String> algorithms, long startOffset, long length, int blockSize) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyDataExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyDataExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyDataExtension.java
new file mode 100644
index 0000000..0250b86
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyDataExtension.java
@@ -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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
+
+/**
+ * Implements the "copy-data" extension
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://tools.ietf.org/id/draft-ietf-secsh-filexfer-extensions-00.txt">DRAFT 00 section 7</A>
+ */
+public interface CopyDataExtension extends SftpClientExtension {
+ void copyData(Handle readHandle, long readOffset, long readLength, Handle writeHandle, long writeOffset) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyFileExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyFileExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyFileExtension.java
new file mode 100644
index 0000000..749c1a6
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/CopyFileExtension.java
@@ -0,0 +1,36 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-extensions-00#section-6">copy-file extension</A>
+ */
+public interface CopyFileExtension extends SftpClientExtension {
+ /**
+ * @param src The (<U>remote</U>) file source path
+ * @param dst The (<U>remote</U>) file destination path
+ * @param overwriteDestination If {@code true} then OK to override destination if exists
+ * @throws IOException If failed to execute the command or extension not supported
+ */
+ void copyFile(String src, String dst, boolean overwriteDestination) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5FileExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5FileExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5FileExtension.java
new file mode 100644
index 0000000..2e8d23f
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5FileExtension.java
@@ -0,0 +1,40 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@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.1.1</A>
+ */
+public interface MD5FileExtension extends SftpClientExtension {
+ /**
+ * @param path The (remote) path
+ * @param offset The offset to start calculating the hash
+ * @param length The number of data bytes to calculate the hash on - if
+ * greater than available, then up to whatever is available
+ * @param quickHash A quick-hash of the 1st 2048 bytes - ignored if {@code null}/empty
+ * @return The hash value if the quick hash matches (or {@code null}/empty), or
+ * {@code null}/empty if the quick hash is provided and it does not match
+ * @throws IOException If failed to calculate the hash
+ */
+ byte[] getHash(String path, long offset, long length, byte[] quickHash) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5HandleExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5HandleExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5HandleExtension.java
new file mode 100644
index 0000000..18392fa
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/MD5HandleExtension.java
@@ -0,0 +1,43 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+
+/**
+ * @author <a href="mailto:dev@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.1.1</A>
+ */
+public interface MD5HandleExtension extends SftpClientExtension {
+ /**
+ * @param handle The (remote) file {@code Handle}
+ * @param offset The offset to start calculating the hash
+ * @param length The number of data bytes to calculate the hash on - if
+ * greater than available, then up to whatever is available
+ * @param quickHash A quick-hash of the 1st 2048 bytes - ignored if {@code null}/empty
+ * @return The hash value if the quick hash matches (or {@code null}/empty), or
+ * {@code null}/empty if the quick hash is provided and it does not match
+ * @throws IOException If failed to calculate the hash
+ */
+ byte[] getHash(SftpClient.Handle handle, long offset, long length, byte[] quickHash) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtension.java
new file mode 100644
index 0000000..c27a9e1
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtension.java
@@ -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.client.subsystem.sftp.extensions;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SftpClientExtension extends NamedResource, OptionalFeature {
+ /**
+ * @return The {@link SftpClient} used to issue the extended command
+ */
+ SftpClient getClient();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtensionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtensionFactory.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtensionFactory.java
new file mode 100644
index 0000000..0692a04
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SftpClientExtensionFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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.client.subsystem.sftp.extensions;
+
+import java.util.Map;
+
+import org.apache.sshd.client.subsystem.sftp.RawSftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.subsystem.sftp.extensions.ParserUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SftpClientExtensionFactory extends NamedResource {
+ default SftpClientExtension create(SftpClient client, RawSftpClient raw) {
+ Map<String, byte[]> extensions = client.getServerExtensions();
+ return create(client, raw, extensions, ParserUtils.parse(extensions));
+ }
+
+ SftpClientExtension create(SftpClient client, RawSftpClient raw, Map<String, byte[]> extensions, Map<String, ?> parsed);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SpaceAvailableExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SpaceAvailableExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SpaceAvailableExtension.java
new file mode 100644
index 0000000..2cc938b
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/SpaceAvailableExtension.java
@@ -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.client.subsystem.sftp.extensions;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.subsystem.sftp.extensions.SpaceAvailableExtensionInfo;
+
+/**
+ * Implements the "space-available" extension
+ *
+ * @author <a href="mailto:dev@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 interface SpaceAvailableExtension extends SftpClientExtension {
+ SpaceAvailableExtensionInfo available(String path) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtension.java
new file mode 100644
index 0000000..1411098
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtension.java
@@ -0,0 +1,76 @@
+/*
+ * 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.client.subsystem.sftp.extensions.helpers;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.apache.sshd.client.subsystem.sftp.RawSftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractCheckFileExtension extends AbstractSftpClientExtension {
+ protected AbstractCheckFileExtension(String name, SftpClient client, RawSftpClient raw, Collection<String> extras) {
+ super(name, client, raw, extras);
+ }
+
+ protected SimpleImmutableEntry<String, Collection<byte[]>> doGetHash(Object target, Collection<String> algorithms, long offset, long length, int blockSize) throws IOException {
+ Buffer buffer = getCommandBuffer(target, Byte.MAX_VALUE);
+ putTarget(buffer, target);
+ buffer.putString(GenericUtils.join(algorithms, ','));
+ buffer.putLong(offset);
+ buffer.putLong(length);
+ buffer.putInt(blockSize);
+
+ if (log.isDebugEnabled()) {
+ log.debug("doGetHash({})[{}] - offset={}, length={}, block-size={}",
+ getName(), (target instanceof CharSequence) ? target : BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, (byte[]) target),
+ offset, length, blockSize);
+ }
+
+ buffer = checkExtendedReplyBuffer(receive(sendExtendedCommand(buffer)));
+ if (buffer == null) {
+ throw new StreamCorruptedException("Missing extended reply data");
+ }
+
+ String targetType = buffer.getString();
+ if (String.CASE_INSENSITIVE_ORDER.compare(targetType, SftpConstants.EXT_CHECK_FILE) != 0) {
+ throw new StreamCorruptedException("Mismatched reply type: expected=" + SftpConstants.EXT_CHECK_FILE + ", actual=" + targetType);
+ }
+
+ String algo = buffer.getString();
+ Collection<byte[]> hashes = new LinkedList<>();
+ while (buffer.available() > 0) {
+ byte[] hashValue = buffer.getBytes();
+ hashes.add(hashValue);
+ }
+
+ return new SimpleImmutableEntry<>(algo, hashes);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtension.java
new file mode 100644
index 0000000..ab00f9e
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtension.java
@@ -0,0 +1,75 @@
+/*
+ * 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.client.subsystem.sftp.extensions.helpers;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.util.Collection;
+
+import org.apache.sshd.client.subsystem.sftp.RawSftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractMD5HashExtension extends AbstractSftpClientExtension {
+ protected AbstractMD5HashExtension(String name, SftpClient client, RawSftpClient raw, Collection<String> extras) {
+ super(name, client, raw, extras);
+ }
+
+ protected byte[] doGetHash(Object target, long offset, long length, byte[] quickHash) throws IOException {
+ Buffer buffer = getCommandBuffer(target, Long.SIZE + 2 * Long.BYTES + Integer.BYTES + NumberUtils.length(quickHash));
+ String opcode = getName();
+ putTarget(buffer, target);
+ buffer.putLong(offset);
+ buffer.putLong(length);
+ buffer.putBytes((quickHash == null) ? GenericUtils.EMPTY_BYTE_ARRAY : quickHash);
+
+ boolean debugEnabled = log.isDebugEnabled();
+ if (debugEnabled) {
+ log.debug("doGetHash({})[{}] - offset={}, length={}, quick-hash={}",
+ opcode, (target instanceof CharSequence) ? target : BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, (byte[]) target),
+ offset, length, BufferUtils.toHex(':', quickHash));
+ }
+
+ buffer = checkExtendedReplyBuffer(receive(sendExtendedCommand(buffer)));
+ if (buffer == null) {
+ throw new StreamCorruptedException("Missing extended reply data");
+ }
+
+ String targetType = buffer.getString();
+ if (String.CASE_INSENSITIVE_ORDER.compare(targetType, opcode) != 0) {
+ throw new StreamCorruptedException("Mismatched reply target type: expected=" + opcode + ", actual=" + targetType);
+ }
+
+ byte[] hashValue = buffer.getBytes();
+ if (debugEnabled) {
+ log.debug("doGetHash({})[{}] - offset={}, length={}, quick-hash={} - result={}",
+ opcode, target, offset, length,
+ BufferUtils.toHex(':', quickHash), BufferUtils.toHex(':', hashValue));
+ }
+
+ return hashValue;
+ }
+}