You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2020/08/18 05:49:09 UTC
[mina-sshd] 06/06: [SSHD-1005] Create consistent SCP command
details hierarchy
This is an automated email from the ASF dual-hosted git repository.
lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 39a0841e0c55101d813f658e65c584bb42fef942
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Mon Aug 17 10:18:13 2020 +0300
[SSHD-1005] Create consistent SCP command details hierarchy
---
.../org/apache/sshd/common/util/GenericUtils.java | 28 ++++++-
.../apache/sshd/scp/client/DefaultScpClient.java | 5 +-
.../sshd/scp/client/DefaultScpStreamResolver.java | 8 +-
.../java/org/apache/sshd/scp/client/ScpClient.java | 10 ++-
.../scp/client/ScpRemote2RemoteTransferHelper.java | 10 +--
.../client/ScpRemote2RemoteTransferListener.java | 10 +--
.../org/apache/sshd/scp/common/ScpFileOpener.java | 10 ++-
.../java/org/apache/sshd/scp/common/ScpHelper.java | 17 +++--
.../sshd/scp/common/ScpReceiveLineHandler.java | 5 +-
.../sshd/scp/common/ScpSourceStreamResolver.java | 7 +-
.../sshd/scp/common/ScpTargetStreamResolver.java | 3 +-
.../helpers/LocalFileScpSourceStreamResolver.java | 7 +-
.../helpers/LocalFileScpTargetStreamResolver.java | 5 +-
.../common/helpers/ScpDirEndCommandDetails.java | 28 +++++++
.../apache/sshd/scp/common/helpers/ScpIoUtils.java | 11 ++-
.../helpers/ScpPathCommandDetailsSupport.java | 35 ++++++++-
.../helpers/ScpReceiveDirCommandDetails.java | 7 +-
.../ScpTimestampCommandDetails.java} | 42 ++++++++---
.../client/ScpRemote2RemoteTransferHelperTest.java | 6 +-
.../helpers/AbstractScpCommandDetailsTest.java | 88 ++++++++++++++++++++++
.../server/ScpReceiveDirCommandDetailsTest.java | 49 ++++++++++++
21 files changed, 323 insertions(+), 68 deletions(-)
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index ea2c4e6..f48a49f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -298,6 +298,30 @@ public final class GenericUtils {
return !isEmpty(c);
}
+ /**
+ *
+ * @param <T> Generic element type
+ * @param c1 First collection
+ * @param c2 Second collection
+ * @return {@code true} if the following holds:
+ * <UL>
+ * <LI>Same size - <B>Note:</B> {@code null} collections are consider equal to empty ones</LI>
+ *
+ * <LI>First collection contains all elements of second one and vice versa</LI>
+ * </UL>
+ */
+ public static <T> boolean equals(Collection<T> c1, Collection<T> c2) {
+ if (isEmpty(c1)) {
+ return isEmpty(c2);
+ } else if (isEmpty(c2)) {
+ return false;
+ }
+
+ return (c1.size() == c2.size())
+ && c1.containsAll(c2)
+ && c2.containsAll(c1);
+ }
+
public static int size(Map<?, ?> m) {
return (m == null) ? 0 : m.size();
}
@@ -1039,7 +1063,7 @@ public final class GenericUtils {
/**
* Check if a duration is positive
- *
+ *
* @param d the duration
* @return <code>true</code> if the duration is greater than zero
*/
@@ -1049,7 +1073,7 @@ public final class GenericUtils {
/**
* Check if a duration is negative or zero
- *
+ *
* @param d the duration
* @return <code>true</code> if the duration is negative or zero
*/
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java
index 471ef2d..6a032e8 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java
@@ -38,9 +38,9 @@ import org.apache.sshd.common.file.util.MockPath;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.scp.common.ScpFileOpener;
import org.apache.sshd.scp.common.ScpHelper;
-import org.apache.sshd.scp.common.ScpTimestamp;
import org.apache.sshd.scp.common.ScpTransferEventListener;
import org.apache.sshd.scp.common.helpers.DefaultScpFileOpener;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -102,7 +102,8 @@ public class DefaultScpClient extends AbstractScpClient {
}
@Override
- public void upload(InputStream local, String remote, long size, Collection<PosixFilePermission> perms, ScpTimestamp time)
+ public void upload(
+ InputStream local, String remote, long size, Collection<PosixFilePermission> perms, ScpTimestampCommandDetails time)
throws IOException {
int namePos = ValidateUtils.checkNotNullAndNotEmpty(remote, "No remote location specified").lastIndexOf('/');
String name = (namePos < 0)
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpStreamResolver.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpStreamResolver.java
index fb82cf2..eb21379 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpStreamResolver.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpStreamResolver.java
@@ -28,7 +28,7 @@ import java.util.Set;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.scp.common.ScpSourceStreamResolver;
-import org.apache.sshd.scp.common.ScpTimestamp;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -37,14 +37,14 @@ public class DefaultScpStreamResolver implements ScpSourceStreamResolver {
private final String name;
private final Path mockPath;
private final Collection<PosixFilePermission> perms;
- private final ScpTimestamp time;
+ private final ScpTimestampCommandDetails time;
private final long size;
private final InputStream local;
private final String cmd;
public DefaultScpStreamResolver(
String name, Path mockPath, Collection<PosixFilePermission> perms,
- ScpTimestamp time, long size, InputStream local, String cmd) {
+ ScpTimestampCommandDetails time, long size, InputStream local, String cmd) {
this.name = name;
this.mockPath = mockPath;
this.perms = perms;
@@ -70,7 +70,7 @@ public class DefaultScpStreamResolver implements ScpSourceStreamResolver {
}
@Override
- public ScpTimestamp getTimestamp() throws IOException {
+ public ScpTimestampCommandDetails getTimestamp() throws IOException {
return time;
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpClient.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpClient.java
index c40d07b..ef781bc 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpClient.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpClient.java
@@ -33,7 +33,7 @@ import org.apache.sshd.common.session.SessionHolder;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.scp.common.ScpHelper;
-import org.apache.sshd.scp.common.ScpTimestamp;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -116,20 +116,22 @@ public interface ScpClient extends SessionHolder<ClientSession>, ClientSessionHo
// NOTE: due to SCP command limitations, the amount of data to be uploaded must be known a-priori
// To upload a dynamic amount of data use SFTP
- default void upload(byte[] data, String remote, Collection<PosixFilePermission> perms, ScpTimestamp time)
+ default void upload(byte[] data, String remote, Collection<PosixFilePermission> perms, ScpTimestampCommandDetails time)
throws IOException {
upload(data, 0, data.length, remote, perms, time);
}
default void upload(
- byte[] data, int offset, int len, String remote, Collection<PosixFilePermission> perms, ScpTimestamp time)
+ byte[] data, int offset, int len, String remote, Collection<PosixFilePermission> perms,
+ ScpTimestampCommandDetails time)
throws IOException {
try (InputStream local = new ByteArrayInputStream(data, offset, len)) {
upload(local, remote, len, perms, time);
}
}
- void upload(InputStream local, String remote, long size, Collection<PosixFilePermission> perms, ScpTimestamp time)
+ void upload(
+ InputStream local, String remote, long size, Collection<PosixFilePermission> perms, ScpTimestampCommandDetails time)
throws IOException;
static String createSendCommand(String remote, Collection<Option> options) {
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java
index 1b4852a..d558576 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java
@@ -35,10 +35,10 @@ import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.common.util.io.LimitInputStream;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.scp.client.ScpClient.Option;
-import org.apache.sshd.scp.common.ScpTimestamp;
import org.apache.sshd.scp.common.helpers.AbstractScpCommandDetails;
import org.apache.sshd.scp.common.helpers.ScpIoUtils;
import org.apache.sshd.scp.common.helpers.ScpReceiveFileCommandDetails;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* Helps transfer files between 2 servers rather than between server and local file system by using 2
@@ -133,10 +133,10 @@ public class ScpRemote2RemoteTransferHelper extends AbstractLoggingBean {
}
char cmdName = header.charAt(0);
- ScpTimestamp time = null;
- if (cmdName == ScpTimestamp.COMMAND_NAME) {
+ ScpTimestampCommandDetails time = null;
+ if (cmdName == ScpTimestampCommandDetails.COMMAND_NAME) {
// Pass along the "T<mtime> 0 <atime> 0" and wait for response
- time = ScpTimestamp.parseTime(header);
+ time = ScpTimestampCommandDetails.parseTime(header);
signalReceivedCommand(time);
ScpIoUtils.writeLine(dstOut, header);
@@ -203,7 +203,7 @@ public class ScpRemote2RemoteTransferHelper extends AbstractLoggingBean {
protected long transferFileData(
String source, InputStream srcIn, OutputStream srcOut,
String destination, InputStream dstIn, OutputStream dstOut,
- ScpTimestamp time, ScpReceiveFileCommandDetails details)
+ ScpTimestampCommandDetails time, ScpReceiveFileCommandDetails details)
throws IOException {
long length = details.getLength();
if (length < 0L) { // TODO consider throwing an exception...
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferListener.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferListener.java
index 1322495..8ddad59 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferListener.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferListener.java
@@ -22,8 +22,8 @@ package org.apache.sshd.scp.client;
import java.io.IOException;
import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.scp.common.ScpTimestamp;
import org.apache.sshd.scp.common.helpers.ScpReceiveFileCommandDetails;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -36,14 +36,14 @@ public interface ScpRemote2RemoteTransferListener {
* @param source The source path
* @param dstSession The destination {@link ClientSession}
* @param destination The destination path
- * @param timestamp The {@link ScpTimestamp timestamp} of the file - may be {@code null}
+ * @param timestamp The {@link ScpTimestampCommandDetails timestamp} of the file - may be {@code null}
* @param details The {@link ScpReceiveFileCommandDetails details} of the attempted file transfer
* @throws IOException If failed to handle the callback
*/
void startDirectFileTransfer(
ClientSession srcSession, String source,
ClientSession dstSession, String destination,
- ScpTimestamp timestamp, ScpReceiveFileCommandDetails details)
+ ScpTimestampCommandDetails timestamp, ScpReceiveFileCommandDetails details)
throws IOException;
/**
@@ -53,7 +53,7 @@ public interface ScpRemote2RemoteTransferListener {
* @param source The source path
* @param dstSession The destination {@link ClientSession}
* @param destination The destination path
- * @param timestamp The {@link ScpTimestamp timestamp} of the file - may be {@code null}
+ * @param timestamp The {@link ScpTimestampCommandDetails timestamp} of the file - may be {@code null}
* @param details The {@link ScpReceiveFileCommandDetails details} of the attempted file transfer
* @param xferSize Number of successfully transfered bytes - zero if <tt>thrown</tt> not {@code null}
* @param thrown Error thrown during transfer attempt - {@code null} if successful
@@ -62,7 +62,7 @@ public interface ScpRemote2RemoteTransferListener {
void endDirectFileTransfer(
ClientSession srcSession, String source,
ClientSession dstSession, String destination,
- ScpTimestamp timestamp, ScpReceiveFileCommandDetails details,
+ ScpTimestampCommandDetails timestamp, ScpReceiveFileCommandDetails details,
long xferSize, Throwable thrown)
throws IOException;
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java
index 1967e3e..fec0e66 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpFileOpener.java
@@ -43,6 +43,7 @@ import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.SelectorUtils;
import org.apache.sshd.common.util.io.DirectoryScanner;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* Plug-in mechanism for users to intervene in the SCP process - e.g., apply some kind of traffic shaping mechanism,
@@ -60,14 +61,14 @@ public interface ScpFileOpener {
* @param name The target file name
* @param preserve Whether requested to preserve the permissions and timestamp
* @param permissions The requested file permissions
- * @param time The requested {@link ScpTimestamp} - may be {@code null} if nothing to update
+ * @param time The requested {@link ScpTimestampCommandDetails} - may be {@code null} if nothing to update
* @return The actual target file path for the incoming file/directory
* @throws IOException If failed to resolve the file path
- * @see #updateFileProperties(Path, Set, ScpTimestamp) updateFileProperties
+ * @see #updateFileProperties(Path, Set, ScpTimestampCommandDetails) updateFileProperties
*/
default Path resolveIncomingFilePath(
Session session, Path localPath, String name, boolean preserve, Set<PosixFilePermission> permissions,
- ScpTimestamp time)
+ ScpTimestampCommandDetails time)
throws IOException {
LinkOption[] options = IoUtils.getLinkOptions(true);
Boolean status = IoUtils.checkFileExists(localPath, options);
@@ -331,7 +332,8 @@ public interface ScpFileOpener {
ScpTargetStreamResolver createScpTargetStreamResolver(Session session, Path path) throws IOException;
- static void updateFileProperties(Path file, Set<PosixFilePermission> perms, ScpTimestamp time) throws IOException {
+ static void updateFileProperties(Path file, Set<PosixFilePermission> perms, ScpTimestampCommandDetails time)
+ throws IOException {
IoUtils.setPermissions(file, perms);
if (time != null) {
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java
index 53c3f94..4a6b111 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java
@@ -51,6 +51,7 @@ import org.apache.sshd.scp.common.helpers.ScpIoUtils;
import org.apache.sshd.scp.common.helpers.ScpPathCommandDetailsSupport;
import org.apache.sshd.scp.common.helpers.ScpReceiveDirCommandDetails;
import org.apache.sshd.scp.common.helpers.ScpReceiveFileCommandDetails;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -130,7 +131,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=537593
public void postProcessReceivedData(
String name, boolean preserve, Set<PosixFilePermission> perms,
- ScpTimestamp time)
+ ScpTimestampCommandDetails time)
throws IOException {
if (log.isDebugEnabled()) {
log.debug("postProcessReceivedData({}) name={}, perms={}, preserve={} time={}", ScpHelper.this,
@@ -163,7 +164,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
ScpIoUtils.receive(getSession(), in, out, log, this, handler);
}
- public void receiveDir(String header, Path local, ScpTimestamp time, boolean preserve, int bufferSize)
+ public void receiveDir(String header, Path local, ScpTimestampCommandDetails time, boolean preserve, int bufferSize)
throws IOException {
Path path = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath();
boolean debugEnabled = log.isDebugEnabled();
@@ -205,8 +206,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
} else if (cmdChar == ScpDirEndCommandDetails.COMMAND_NAME) {
ack();
break;
- } else if (cmdChar == ScpTimestamp.COMMAND_NAME) {
- time = ScpTimestamp.parseTime(header);
+ } else if (cmdChar == ScpTimestampCommandDetails.COMMAND_NAME) {
+ time = ScpTimestampCommandDetails.parseTime(header);
ack();
} else {
throw new IOException("Unexpected message: '" + header + "'");
@@ -219,7 +220,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
listener.endFolderEvent(session, FileOperation.RECEIVE, path, perms, null);
}
- public void receiveFile(String header, Path local, ScpTimestamp time, boolean preserve, int bufferSize)
+ public void receiveFile(String header, Path local, ScpTimestampCommandDetails time, boolean preserve, int bufferSize)
throws IOException {
Path path = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath();
if (log.isDebugEnabled()) {
@@ -232,7 +233,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
}
public void receiveStream(
- String header, ScpTargetStreamResolver resolver, ScpTimestamp time, boolean preserve,
+ String header, ScpTargetStreamResolver resolver, ScpTimestampCommandDetails time, boolean preserve,
int bufferSize)
throws IOException {
if (bufferSize < MIN_RECEIVE_BUFFER_SIZE) {
@@ -451,7 +452,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
bufSize = MIN_SEND_BUFFER_SIZE;
}
- ScpTimestamp time = resolver.getTimestamp();
+ ScpTimestampCommandDetails time = resolver.getTimestamp();
if (preserve && (time != null)) {
int readyCode = ScpIoUtils.sendTimeCommand(in, out, time, log, this);
String cmd = time.toHeader();
@@ -530,7 +531,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess
BasicFileAttributes basic = opener.getLocalBasicFileAttributes(session, path, options);
FileTime lastModified = basic.lastModifiedTime();
FileTime lastAccess = basic.lastAccessTime();
- ScpTimestamp time = new ScpTimestamp(lastModified, lastAccess);
+ ScpTimestampCommandDetails time = new ScpTimestampCommandDetails(lastModified, lastAccess);
String cmd = time.toHeader();
if (debugEnabled) {
log.debug("sendDir({})[{}] send last-modified={}, last-access={} command: {}", this, path, lastModified,
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpReceiveLineHandler.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpReceiveLineHandler.java
index 38db119..9aac7da 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpReceiveLineHandler.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpReceiveLineHandler.java
@@ -22,6 +22,7 @@ package org.apache.sshd.scp.common;
import java.io.IOException;
import org.apache.sshd.common.session.Session;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -32,8 +33,8 @@ public interface ScpReceiveLineHandler {
* @param session The client/server {@link Session} through which the transfer is being executed
* @param line Received SCP input line
* @param isDir Does the input line refer to a directory
- * @param time The received {@link ScpTimestamp} - may be {@code null}
+ * @param time The received {@link ScpTimestampCommandDetails} - may be {@code null}
* @throws IOException If failed to process the line
*/
- void process(Session session, String line, boolean isDir, ScpTimestamp time) throws IOException;
+ void process(Session session, String line, boolean isDir, ScpTimestampCommandDetails time) throws IOException;
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpSourceStreamResolver.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpSourceStreamResolver.java
index 0d79d3b..3fe7d26 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpSourceStreamResolver.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpSourceStreamResolver.java
@@ -28,6 +28,7 @@ import java.util.Collection;
import java.util.Set;
import org.apache.sshd.common.session.Session;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -51,11 +52,11 @@ public interface ScpSourceStreamResolver {
Collection<PosixFilePermission> getPermissions() throws IOException;
/**
- * @return The {@link ScpTimestamp} to use for uploading the file if {@code null} then no need to send
- * this information
+ * @return The {@link ScpTimestampCommandDetails} to use for uploading the file if {@code null} then no
+ * need to send this information
* @throws IOException If failed to generate the required data
*/
- ScpTimestamp getTimestamp() throws IOException;
+ ScpTimestampCommandDetails getTimestamp() throws IOException;
/**
* @return An estimated size of the expected number of bytes to be uploaded. If non-positive then
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTargetStreamResolver.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTargetStreamResolver.java
index d894224..5fd7721 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTargetStreamResolver.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTargetStreamResolver.java
@@ -27,6 +27,7 @@ import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
import org.apache.sshd.common.session.Session;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -83,6 +84,6 @@ public interface ScpTargetStreamResolver {
* @throws IOException If failed to post-process the incoming data
*/
void postProcessReceivedData(
- String name, boolean preserve, Set<PosixFilePermission> perms, ScpTimestamp time)
+ String name, boolean preserve, Set<PosixFilePermission> perms, ScpTimestampCommandDetails time)
throws IOException;
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpSourceStreamResolver.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpSourceStreamResolver.java
index 3fca826..a501ad4 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpSourceStreamResolver.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpSourceStreamResolver.java
@@ -36,7 +36,6 @@ import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.scp.common.ScpFileOpener;
import org.apache.sshd.scp.common.ScpSourceStreamResolver;
-import org.apache.sshd.scp.common.ScpTimestamp;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -47,7 +46,7 @@ public class LocalFileScpSourceStreamResolver extends AbstractLoggingBean implem
protected final Path name;
protected final Set<PosixFilePermission> perms;
protected final long size;
- protected final ScpTimestamp time;
+ protected final ScpTimestampCommandDetails time;
public LocalFileScpSourceStreamResolver(Path path, ScpFileOpener opener) throws IOException {
this.path = Objects.requireNonNull(path, "No path specified");
@@ -58,7 +57,7 @@ public class LocalFileScpSourceStreamResolver extends AbstractLoggingBean implem
BasicFileAttributeView view = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes basic = view.readAttributes();
this.size = basic.size();
- this.time = new ScpTimestamp(basic.lastModifiedTime().toMillis(), basic.lastAccessTime().toMillis());
+ this.time = new ScpTimestampCommandDetails(basic.lastModifiedTime().toMillis(), basic.lastAccessTime().toMillis());
}
@Override
@@ -72,7 +71,7 @@ public class LocalFileScpSourceStreamResolver extends AbstractLoggingBean implem
}
@Override
- public ScpTimestamp getTimestamp() throws IOException {
+ public ScpTimestampCommandDetails getTimestamp() throws IOException {
return time;
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpTargetStreamResolver.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpTargetStreamResolver.java
index 523c7aa..c04aa04 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpTargetStreamResolver.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/LocalFileScpTargetStreamResolver.java
@@ -39,7 +39,6 @@ import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.scp.common.ScpFileOpener;
import org.apache.sshd.scp.common.ScpTargetStreamResolver;
-import org.apache.sshd.scp.common.ScpTimestamp;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -137,7 +136,7 @@ public class LocalFileScpTargetStreamResolver extends AbstractLoggingBean implem
@Override
public void postProcessReceivedData(
- String name, boolean preserve, Set<PosixFilePermission> perms, ScpTimestamp time)
+ String name, boolean preserve, Set<PosixFilePermission> perms, ScpTimestampCommandDetails time)
throws IOException {
if (file == null) {
throw new StreamCorruptedException(
@@ -150,7 +149,7 @@ public class LocalFileScpTargetStreamResolver extends AbstractLoggingBean implem
}
protected void updateFileProperties(
- String name, Path path, Set<PosixFilePermission> perms, ScpTimestamp time)
+ String name, Path path, Set<PosixFilePermission> perms, ScpTimestampCommandDetails time)
throws IOException {
boolean traceEnabled = log.isTraceEnabled();
if (traceEnabled) {
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpDirEndCommandDetails.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpDirEndCommandDetails.java
index b1a638f..3075384 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpDirEndCommandDetails.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpDirEndCommandDetails.java
@@ -32,8 +32,36 @@ public class ScpDirEndCommandDetails extends AbstractScpCommandDetails {
super(COMMAND_NAME);
}
+ public ScpDirEndCommandDetails(String header) {
+ super(COMMAND_NAME);
+ if (!HEADER.equals(header)) {
+ throw new IllegalArgumentException("Mismatched header - expected '" + HEADER + "' but got '" + header + "'");
+ }
+ }
+
@Override
public String toHeader() {
return HEADER;
}
+
+ @Override
+ public int hashCode() {
+ return HEADER.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ // All ScpDirEndCommandDetails are equal to each other
+ return true;
+ }
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpIoUtils.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpIoUtils.java
index b8e271c..0f95834 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpIoUtils.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpIoUtils.java
@@ -41,7 +41,6 @@ import org.apache.sshd.core.CoreModuleProperties;
import org.apache.sshd.scp.ScpModuleProperties;
import org.apache.sshd.scp.common.ScpException;
import org.apache.sshd.scp.common.ScpReceiveLineHandler;
-import org.apache.sshd.scp.common.ScpTimestamp;
import org.slf4j.Logger;
/**
@@ -93,14 +92,14 @@ public final class ScpIoUtils {
*
* @param in The {@link InputStream} to read from
* @param out The target {@link OutputStream}
- * @param time The {@link ScpTimestamp} value to send
+ * @param time The {@link ScpTimestampCommandDetails} value to send
* @param log An optional {@link Logger} to use for issuing log messages - ignored if {@code null}
* @param logHint An optional hint to be used in the logged messages to identifier the caller's context
* @return The read ACK value
* @throws IOException If failed to complete the read/write cyle
*/
public static int sendTimeCommand(
- InputStream in, OutputStream out, ScpTimestamp time, Logger log, Object logHint)
+ InputStream in, OutputStream out, ScpTimestampCommandDetails time, Logger log, Object logHint)
throws IOException {
String cmd = time.toHeader();
if ((log != null) && log.isDebugEnabled()) {
@@ -201,7 +200,7 @@ public final class ScpIoUtils {
ack(out);
boolean debugEnabled = (log != null) && log.isDebugEnabled();
- for (ScpTimestamp time = null;;) {
+ for (ScpTimestampCommandDetails time = null;;) {
String line;
boolean isDir = false;
int c = readAck(in, true, log, logHint);
@@ -223,13 +222,13 @@ public final class ScpIoUtils {
log.debug("receive({}) - Received 'C' header: {}", logHint, line);
}
break;
- case ScpTimestamp.COMMAND_NAME:
+ case ScpTimestampCommandDetails.COMMAND_NAME:
line = readLine(in);
line = Character.toString((char) c) + line;
if (debugEnabled) {
log.debug("receive({}) - Received 'T' header: {}", logHint, line);
}
- time = ScpTimestamp.parseTime(line);
+ time = ScpTimestampCommandDetails.parseTime(line);
ack(out);
continue;
case ScpDirEndCommandDetails.COMMAND_NAME:
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpPathCommandDetailsSupport.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpPathCommandDetailsSupport.java
index 5197656..b2a8dfc 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpPathCommandDetailsSupport.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpPathCommandDetailsSupport.java
@@ -22,9 +22,11 @@ package org.apache.sshd.scp.common.helpers;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Collection;
import java.util.EnumSet;
+import java.util.Objects;
import java.util.Set;
import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
/**
@@ -76,6 +78,10 @@ public abstract class ScpPathCommandDetailsSupport extends AbstractScpCommandDet
return length;
}
+ protected long getEffectiveLength() {
+ return getLength();
+ }
+
public void setLength(long length) {
this.length = length;
}
@@ -91,7 +97,34 @@ public abstract class ScpPathCommandDetailsSupport extends AbstractScpCommandDet
@Override
public String toHeader() {
- return getCommand() + getOctalPermissions(getPermissions()) + " " + getLength() + " " + getName();
+ return getCommand() + getOctalPermissions(getPermissions()) + " " + getEffectiveLength() + " " + getName();
+ }
+
+ @Override
+ public int hashCode() {
+ return Character.hashCode(getCommand())
+ + 31 * Objects.hashCode(getName())
+ + 37 * Long.hashCode(getEffectiveLength())
+ + 41 * GenericUtils.size(getPermissions());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ ScpPathCommandDetailsSupport other = (ScpPathCommandDetailsSupport) obj;
+ return (getCommand() == other.getCommand())
+ && (getEffectiveLength() == other.getEffectiveLength())
+ && Objects.equals(getName(), other.getName())
+ && GenericUtils.equals(getPermissions(), other.getPermissions());
}
@Override
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpReceiveDirCommandDetails.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpReceiveDirCommandDetails.java
index f9f67ee..efcc77f 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpReceiveDirCommandDetails.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpReceiveDirCommandDetails.java
@@ -23,7 +23,7 @@ import org.apache.sshd.common.util.GenericUtils;
/**
* Holds the details of a "Dmmmm <length> <directory>" command - e.g., "D0755 0 dirname"
- *
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class ScpReceiveDirCommandDetails extends ScpPathCommandDetailsSupport {
@@ -38,6 +38,11 @@ public class ScpReceiveDirCommandDetails extends ScpPathCommandDetailsSupport {
super(COMMAND_NAME, header);
}
+ @Override // length is irrelevant for 'D' commands
+ protected long getEffectiveLength() {
+ return 0L;
+ }
+
public static ScpReceiveDirCommandDetails parse(String header) {
return GenericUtils.isEmpty(header) ? null : new ScpReceiveDirCommandDetails(header);
}
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTimestamp.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpTimestampCommandDetails.java
similarity index 70%
rename from sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTimestamp.java
rename to sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpTimestampCommandDetails.java
index 23fcb5d..e1a085d 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTimestamp.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/helpers/ScpTimestampCommandDetails.java
@@ -17,27 +17,26 @@
* under the License.
*/
-package org.apache.sshd.scp.common;
+package org.apache.sshd.scp.common.helpers;
import java.nio.file.attribute.FileTime;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.scp.common.helpers.AbstractScpCommandDetails;
/**
* Represents an SCP timestamp definition
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class ScpTimestamp extends AbstractScpCommandDetails {
+public class ScpTimestampCommandDetails extends AbstractScpCommandDetails {
public static final char COMMAND_NAME = 'T';
private final long lastModifiedTime;
private final long lastAccessTime;
- public ScpTimestamp(String header) {
+ public ScpTimestampCommandDetails(String header) {
super(COMMAND_NAME);
if (header.charAt(0) != COMMAND_NAME) {
@@ -49,11 +48,11 @@ public class ScpTimestamp extends AbstractScpCommandDetails {
lastAccessTime = TimeUnit.SECONDS.toMillis(Long.parseLong(numbers[2]));
}
- public ScpTimestamp(FileTime modTime, FileTime accTime) {
+ public ScpTimestampCommandDetails(FileTime modTime, FileTime accTime) {
this(modTime.to(TimeUnit.MILLISECONDS), accTime.to(TimeUnit.MILLISECONDS));
}
- public ScpTimestamp(long modTime, long accTime) {
+ public ScpTimestampCommandDetails(long modTime, long accTime) {
super(COMMAND_NAME);
lastModifiedTime = modTime;
@@ -71,7 +70,29 @@ public class ScpTimestamp extends AbstractScpCommandDetails {
@Override
public String toHeader() {
return Character.toString(getCommand()) + TimeUnit.MILLISECONDS.toSeconds(getLastModifiedTime())
- + " 0 " + TimeUnit.MILLISECONDS.toSeconds(getLastAccessTime()) + "0";
+ + " 0 " + TimeUnit.MILLISECONDS.toSeconds(getLastAccessTime()) + " 0";
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(getLastModifiedTime()) + 31 * Long.hashCode(getLastAccessTime());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ ScpTimestampCommandDetails other = (ScpTimestampCommandDetails) obj;
+ return (getLastModifiedTime() == other.getLastModifiedTime())
+ && (getLastAccessTime() == other.getLastAccessTime());
}
@Override
@@ -84,12 +105,13 @@ public class ScpTimestamp extends AbstractScpCommandDetails {
* @param line The time specification - format:
* {@code T<mtime-sec> <mtime-micros> <atime-sec> <atime-micros>} where specified
* times are in seconds since UTC - ignored if {@code null}
- * @return The {@link ScpTimestamp} value with the timestamps converted to <U>milliseconds</U>
+ * @return The {@link ScpTimestampCommandDetails} value with the timestamps converted to
+ * <U>milliseconds</U>
* @throws NumberFormatException if bad numerical values - <B>Note:</B> validates that 1st character is 'T'.
* @see <A HREF="https://blogs.oracle.com/janp/entry/how_the_scp_protocol_works">How the
* SCP protocol works</A>
*/
- public static ScpTimestamp parseTime(String line) throws NumberFormatException {
- return GenericUtils.isEmpty(line) ? null : new ScpTimestamp(line);
+ public static ScpTimestampCommandDetails parseTime(String line) throws NumberFormatException {
+ return GenericUtils.isEmpty(line) ? null : new ScpTimestampCommandDetails(line);
}
}
diff --git a/sshd-scp/src/test/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelperTest.java b/sshd-scp/src/test/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelperTest.java
index 7c0508b..f28739c 100644
--- a/sshd-scp/src/test/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelperTest.java
+++ b/sshd-scp/src/test/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelperTest.java
@@ -27,8 +27,8 @@ import java.util.concurrent.atomic.AtomicLong;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.scp.common.ScpHelper;
-import org.apache.sshd.scp.common.ScpTimestamp;
import org.apache.sshd.scp.common.helpers.ScpReceiveFileCommandDetails;
+import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -76,7 +76,7 @@ public class ScpRemote2RemoteTransferHelperTest extends AbstractScpTestSupport {
public void startDirectFileTransfer(
ClientSession srcSession, String source,
ClientSession dstSession, String destination,
- ScpTimestamp timestamp, ScpReceiveFileCommandDetails details)
+ ScpTimestampCommandDetails timestamp, ScpReceiveFileCommandDetails details)
throws IOException {
assertEquals("Mismatched start xfer source path", srcPath, source);
assertEquals("Mismatched start xfer destination path", dstPath, destination);
@@ -86,7 +86,7 @@ public class ScpRemote2RemoteTransferHelperTest extends AbstractScpTestSupport {
public void endDirectFileTransfer(
ClientSession srcSession, String source,
ClientSession dstSession, String destination,
- ScpTimestamp timestamp, ScpReceiveFileCommandDetails details,
+ ScpTimestampCommandDetails timestamp, ScpReceiveFileCommandDetails details,
long xferSize, Throwable thrown)
throws IOException {
assertEquals("Mismatched end xfer source path", srcPath, source);
diff --git a/sshd-scp/src/test/java/org/apache/sshd/scp/common/helpers/AbstractScpCommandDetailsTest.java b/sshd-scp/src/test/java/org/apache/sshd/scp/common/helpers/AbstractScpCommandDetailsTest.java
new file mode 100644
index 0000000..8bfcfc7
--- /dev/null
+++ b/sshd-scp/src/test/java/org/apache/sshd/scp/common/helpers/AbstractScpCommandDetailsTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.scp.common.helpers;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @param <C> Generic {@link AbstractScpCommandDetails} type
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+public class AbstractScpCommandDetailsTest<C extends AbstractScpCommandDetails> extends JUnitTestSupport {
+ private final String header;
+ private final Constructor<C> ctor;
+
+ public AbstractScpCommandDetailsTest(String header, Class<C> cmdClass) throws Exception {
+ this.header = header;
+ this.ctor = cmdClass.getDeclaredConstructor(String.class);
+ }
+
+ @Parameters(name = "cmd={0}")
+ public static List<Object[]> parameters() {
+ return new ArrayList<Object[]>() {
+ // not serializing it
+ private static final long serialVersionUID = 1L;
+
+ {
+ addTestCase("T123456789 0 987654321 0", ScpTimestampCommandDetails.class);
+ addTestCase("C0644 12345 file", ScpReceiveFileCommandDetails.class);
+ addTestCase("D0755 0 dir", ScpReceiveDirCommandDetails.class);
+ addTestCase(ScpDirEndCommandDetails.HEADER, ScpDirEndCommandDetails.class);
+ }
+
+ private void addTestCase(String header, Class<? extends AbstractScpCommandDetails> cmdClass) {
+ add(new Object[] { header, cmdClass });
+ }
+ };
+ }
+
+ @Test
+ public void testHeaderEquality() throws Exception {
+ C details = ctor.newInstance(header);
+ assertEquals(header, details.toHeader());
+ }
+
+ @Test
+ public void testDetailsEquality() throws Exception {
+ C d1 = ctor.newInstance(header);
+ C d2 = ctor.newInstance(header);
+ assertEquals("HASH ?", d1.hashCode(), d2.hashCode());
+ assertEquals("EQ ?", d1, d2);
+ }
+}
diff --git a/sshd-scp/src/test/java/org/apache/sshd/scp/server/ScpReceiveDirCommandDetailsTest.java b/sshd-scp/src/test/java/org/apache/sshd/scp/server/ScpReceiveDirCommandDetailsTest.java
new file mode 100644
index 0000000..78bccf3
--- /dev/null
+++ b/sshd-scp/src/test/java/org/apache/sshd/scp/server/ScpReceiveDirCommandDetailsTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.scp.server;
+
+import org.apache.sshd.scp.common.helpers.ScpReceiveDirCommandDetails;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ScpReceiveDirCommandDetailsTest extends JUnitTestSupport {
+ public ScpReceiveDirCommandDetailsTest() {
+ super();
+ }
+
+ @Test
+ public void testLengthDoesNotInfluenceEquality() {
+ ScpReceiveDirCommandDetails d1 = new ScpReceiveDirCommandDetails("D0555 0 " + getCurrentTestName());
+ ScpReceiveDirCommandDetails d2 = new ScpReceiveDirCommandDetails(d1.toHeader());
+ d2.setLength(d1.getLength() + 1234L);
+ assertNotEquals("Len ?", d1.getLength(), d2.getLength());
+ assertEquals("Hash ?", d1.hashCode(), d2.hashCode());
+ assertEquals("EQ", d1, d2);
+ }
+}