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
The following commit(s) were added to refs/heads/master by this push:
new da29581 [SSHD-1202] Provide SftpErrorDataHandler callback support for
SFTP client
da29581 is described below
commit da2958172281ef20e51681afe036bd42c9cf843a
Author: Lyor Goldstein <[email protected]>
AuthorDate: Fri Aug 6 08:10:52 2021 +0300
[SSHD-1202] Provide SftpErrorDataHandler callback support for SFTP client
---
CHANGES.md | 1 +
docs/sftp.md | 22 ++++
.../main/java/org/apache/sshd/cli/CliLogger.java | 2 +-
.../org/apache/sshd/cli/client/ScpCommandMain.java | 2 +-
.../apache/sshd/cli/client/SftpCommandMain.java | 37 ++++++-
.../sshd/cli/client/SshClientCliSupport.java | 2 +-
.../org/apache/sshd/cli/client/SshClientMain.java | 4 +-
.../org/apache/sshd/cli/client/SshKeyScanMain.java | 2 +-
.../apache/sshd/cli/client/ChannelExecMain.java | 2 +-
.../sshd/client/config/hosts/HostConfigEntry.java | 6 +-
.../sshd/client/config/hosts/KnownHostEntry.java | 4 +-
.../common/config/ConfigFileReaderSupport.java | 4 +-
.../common/config/keys/AuthorizedKeyEntry.java | 4 +-
.../common/config/keys/PrivateKeyEntryDecoder.java | 2 +-
.../openssh/OpenSSHDSSPrivateKeyEntryDecoder.java | 2 +-
.../OpenSSHECDSAPrivateKeyEntryDecoder.java | 2 +-
.../openssh/OpenSSHRSAPrivateKeyDecoder.java | 2 +-
.../loader/pem/DSSPEMResourceKeyPairParser.java | 2 +-
.../loader/pem/ECDSAPEMResourceKeyPairParser.java | 2 +-
.../loader/pem/RSAPEMResourceKeyPairParser.java | 2 +-
.../openssh/OpenSSHKeyPairResourceWriter.java | 2 +-
.../sshd/common/util/buffer/BufferUtils.java | 35 ++++++
.../{NoCloseReader.java => LineDataConsumer.java} | 34 +++---
.../io/{ => input}/CloseableEmptyInputStream.java | 2 +-
.../util/io/{ => input}/EmptyInputStream.java | 2 +-
.../io/{ => input}/InputStreamWithChannel.java | 2 +-
.../util/io/{ => input}/LimitInputStream.java | 2 +-
.../util/io/{ => input}/NoCloseInputStream.java | 2 +-
.../common/util/io/{ => input}/NoCloseReader.java | 2 +-
.../util/io/{ => input}/NullInputStream.java | 2 +-
.../common/util/io/output/LineLevelAppender.java | 118 +++++++++++++++++++++
.../util/io/output/LineLevelAppenderStream.java | 99 +++++++++++++++++
.../common/util/io/output}/LineOutputStream.java | 2 +-
.../io/{ => output}/LoggingFilterOutputStream.java | 2 +-
.../util/io/{ => output}/NoCloseOutputStream.java | 2 +-
.../common/util/io/{ => output}/NoCloseWriter.java | 2 +-
.../util/io/{ => output}/NullOutputStream.java | 2 +-
.../util/io/{ => output}/NullPrintStream.java | 2 +-
.../io/{ => output}/OutputStreamWithChannel.java | 2 +-
.../{ => output}/SecureByteArrayOutputStream.java | 2 +-
.../eddsa/Ed25519PEMResourceKeyParser.java | 2 +-
.../OpenSSHEd25519PrivateKeyEntryDecoder.java | 2 +-
.../openssh/OpenSSHKeyPairResourceWriterTest.java | 2 +-
.../util/io/{ => input}/EmptyInputStreamTest.java | 2 +-
.../util/io/{ => input}/LimitInputStreamTest.java | 2 +-
.../io/{ => input}/NoCloseInputStreamTest.java | 2 +-
.../util/io/{ => input}/NoCloseReaderTest.java | 2 +-
.../util/io/{ => input}/NullInputStreamTest.java | 2 +-
.../util/io/output}/LineOutputStreamTest.java | 2 +-
.../io/{ => output}/NoCloseOutputStreamTest.java | 2 +-
.../util/io/{ => output}/NoCloseWriterTest.java | 2 +-
.../util/io/{ => output}/NullOutputStreamTest.java | 2 +-
.../EndlessTarpitSenderSupportDevelopment.java | 2 +-
.../apache/sshd/client/session/ClientSession.java | 4 +-
.../apache/sshd/server/channel/ChannelSession.java | 2 +-
.../sshd/server/channel/PipeDataReceiver.java | 2 +-
.../java/org/apache/sshd/KeyReExchangeTest.java | 2 +-
.../java/org/apache/sshd/WindowAdjustTest.java | 2 +-
.../java/org/apache/sshd/client/ClientTest.java | 2 +-
.../config/keys/AuthorizedKeysTestSupport.java | 4 +-
.../scp/client/ScpRemote2RemoteTransferHelper.java | 2 +-
.../java/org/apache/sshd/scp/common/ScpHelper.java | 2 +-
.../apache/sshd/sftp/client/SftpClientFactory.java | 54 ++++++++--
.../sshd/sftp/client/SftpErrorDataHandler.java | 36 +++----
.../apache/sshd/sftp/client/fs/SftpFileSystem.java | 21 +++-
.../fs/SftpFileSystemClientSessionInitializer.java | 20 ++--
.../sftp/client/fs/SftpFileSystemProvider.java | 44 ++++++--
.../sshd/sftp/client/impl/AbstractSftpClient.java | 21 +++-
.../sshd/sftp/client/impl/DefaultSftpClient.java | 45 +++++---
.../sftp/client/impl/DefaultSftpClientFactory.java | 17 +--
.../sftp/client/impl/SftpInputStreamAsync.java | 2 +-
.../sftp/client/impl/SftpOutputStreamAsync.java | 2 +-
.../sftp/client/SftpInputStreamWithChannel.java | 2 +-
.../sftp/client/SftpOutputStreamWithChannel.java | 2 +-
74 files changed, 582 insertions(+), 156 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 531127d..9c4838c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,3 +33,4 @@
* [SSHD-1168](https://issues.apache.org/jira/browse/SSHD-1168) OpenSSH
certificates: check certificate type
* [SSHD-1171](https://issues.apache.org/jira/browse/SSHD-1171)
OpenSSHCertificatesTest: certificates expire in 2030
* [SSHD-1172](https://issues.apache.org/jira/browse/SSHD-1172) Expiration of
OpenSshCertificates needs to compare timestamps as unsigned long
+* [SSHD-1202](https://issues.apache.org/jira/browse/SSHD-1202) Provide
SftpErrorDataHandler callback support for SFTP client.
diff --git a/docs/sftp.md b/docs/sftp.md
index 4d60f86..99a2625 100644
--- a/docs/sftp.md
+++ b/docs/sftp.md
@@ -228,6 +228,28 @@ configuration key. The same can be achieved for the CLI
SSHD code by specifying
For more advanced restrictions one needs to sub-class `SftpSubSystem` and
provide a non-default `SftpSubsystemFactory` that uses the sub-classed code.
+### Intercepting data sent via STDERR channel data from the server
+
+According to [SFTP version 4 - section
3.1](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-3.1) the
server MAY send error data through the STDERR pipeline.
+By default, the code ignores such data - however, users may register a
`SftpErrorDataHandler` that will be invoked whenever such data is received from
the server.
+
+```java
+ClientSession session = ...establish a session...
+SftpClientFactory factory = ...obtain a factory instance...
+
+try (SftpClient client = factory.createSftpClient(session, new
MySftpErrorDataHandler())) {
+ ...
+}
+```
+
+The same applies to the `SftpFileSystem` - users may provide a custom error
data handler that will be invoked whenever such data is received from the
server.
+
+**Note:**
+
+* Error data handling must be **short** or it will cause the SSH session to
hang - any long/blocking processing must be done in a separate thread.
+* The provided data buffer contents must be **copied** if they need to be used
after the callback returns as the buffer contents might be re-used by the
caller code.
+* Any exception thrown during handling of the data will cause the SFTP session
to terminate.
+
### Using `SftpFileSystemProvider` to create an `SftpFileSystem`
The code automatically registers the `SftpFileSystemProvider` as the handler
for `sftp://` URL(s). Such URLs are
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
index 04c2019..9b0c064 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
@@ -32,7 +32,7 @@ import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.LogLevelValue;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.NullPrintStream;
+import org.apache.sshd.common.util.io.output.NullPrintStream;
import org.apache.sshd.common.util.logging.SimplifiedLog;
import org.apache.sshd.common.util.logging.SimplifiedLoggerSkeleton;
import org.slf4j.Logger;
diff --git
a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
index c72f741..b089386 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
@@ -46,7 +46,7 @@ import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ReflectionUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.scp.client.ScpClient;
import org.apache.sshd.scp.client.ScpClient.Option;
diff --git
a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
index f81d4b2..feb5d98 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
@@ -29,6 +29,7 @@ import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.channels.Channel;
+import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -66,7 +67,10 @@ import org.apache.sshd.common.util.ReflectionUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.output.LineLevelAppender;
+import org.apache.sshd.common.util.io.output.LineLevelAppenderStream;
+import org.apache.sshd.common.util.io.output.NullOutputStream;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.server.config.SshServerConfigFileReader;
import org.apache.sshd.sftp.client.SftpClient;
@@ -317,6 +321,33 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
return SftpVersionSelector.resolveVersionSelector(selector);
}
+ private static OutputStream resolveErrorDataHandlerStream(ClientSession
session, Logger logger) {
+ String errCharset = session.getString("SftpErrorHandlerOutputCharset");
+ if (GenericUtils.safeCompare(errCharset, "NONE", false) == 0) {
+ return new NullOutputStream();
+ }
+
+ LineLevelAppender appender = new LineLevelAppender() {
+ @Override
+ public boolean isWriteEnabled() {
+ return logger.isErrorEnabled();
+ }
+
+ @Override
+ public void writeLineData(CharSequence lineData) throws
IOException {
+ logger.error("errorChannel: {}", lineData);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignored
+ }
+ };
+ return GenericUtils.isBlank(errCharset)
+ ? new LineLevelAppenderStream(StandardCharsets.US_ASCII,
appender)
+ : new LineLevelAppenderStream(errCharset, appender);
+ }
+
/* -------------------------------------------------------------------- */
public static void main(String[] args) throws Exception {
@@ -356,7 +387,9 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
logger.info("Using version selector={}", versionSelector);
}
- try (SftpClient sftpClient =
clientFactory.createSftpClient(session, versionSelector);
+ try (OutputStream errStream =
resolveErrorDataHandlerStream(session, logger);
+ SftpClient sftpClient = clientFactory.createSftpClient(
+ session, versionSelector, errStream::write);
SftpCommandMain sftp = new SftpCommandMain(sftpClient)) {
// TODO allow injection of extra CommandExecutor(s) via
command line and/or service loading
sftp.doInteractive(stdin, stdout, stderr);
diff --git
a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 4af2024..82f87d8 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -82,7 +82,7 @@ import org.apache.sshd.common.util.MapEntryUtils;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.ReflectionUtils;
import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.common.util.threads.ThreadUtils;
diff --git
a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
index 20f86fa..aa9e438 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
@@ -40,8 +40,8 @@ import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.channel.PtyChannelConfiguration;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.slf4j.Logger;
diff --git
a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
index 0686938..e3b1205 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
@@ -82,7 +82,7 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.MapEntryUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.logging.SimplifiedLog;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.common.util.security.SecurityUtils;
diff --git
a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
index e1cfc31..ec09974 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
@@ -28,7 +28,7 @@ import org.apache.sshd.cli.CliLogger;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.util.test.BaseTestSupport;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
index ad8a604..88f8d93 100644
---
a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++
b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
@@ -56,9 +56,9 @@ import
org.apache.sshd.common.util.MapEntryUtils.NavigableMapBuilder;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseReader;
+import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
/**
* Represents an entry in the client's configuration file as defined by the
diff --git
a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
index 077c00d..03427c6 100644
---
a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
+++
b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
@@ -39,8 +39,8 @@ import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseReader;
/**
* Contains a representation of an entry in the <code>known_hosts</code> file
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
index 9f9ebdf..8a5b248 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
@@ -35,8 +35,8 @@ import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseReader;
import org.apache.sshd.common.util.net.SshdSocketAddress;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
index 2628136..096fd0c 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
@@ -45,8 +45,8 @@ import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.MapEntryUtils;
import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseReader;
/**
* Represents an entry in the user's {@code authorized_keys} file according to
the
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
index 0e61acc..c662720 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
@@ -35,7 +35,7 @@ import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
/**
* @param <PUB> Type of {@link PublicKey}
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
index b543ea9..074a522 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
@@ -41,7 +41,7 @@ import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
index ee33129..3535fe7 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
@@ -43,7 +43,7 @@ import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
index 096a413..1df1d52 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
@@ -41,7 +41,7 @@ import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
index 796bf60..0a7aa05 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
@@ -39,10 +39,10 @@ import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
import org.apache.sshd.common.util.io.der.ASN1Object;
import org.apache.sshd.common.util.io.der.ASN1Type;
import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
index 08fe839..9ed808d 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
@@ -43,10 +43,10 @@ import org.apache.sshd.common.cipher.ECCurves;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
import org.apache.sshd.common.util.io.der.ASN1Object;
import org.apache.sshd.common.util.io.der.ASN1Type;
import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
index 161a0ac..40f8ed6 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
@@ -40,10 +40,10 @@ import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
import org.apache.sshd.common.util.io.der.ASN1Object;
import org.apache.sshd.common.util.io.der.ASN1Type;
import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java
index dd91e85..f9402c4 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java
@@ -51,7 +51,7 @@ import
org.apache.sshd.common.config.keys.loader.openssh.kdf.BCrypt;
import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions;
import org.apache.sshd.common.config.keys.writer.KeyPairResourceWriter;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
/**
* A {@link KeyPairResourceWriter} for writing keys in the modern OpenSSH
format, using the OpenBSD bcrypt KDF for
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
index 8c935d1..5fce736 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -65,6 +65,41 @@ public final class BufferUtils {
throw new UnsupportedOperationException("No instance allowed");
}
+ /**
+ * <p>
+ * Finds the index of the given value in the array starting at the given
index and checking up to specified number
+ * of elements.
+ * </p>
+ *
+ * <p>
+ * This method returns {@code -1}) for a {@code null} input array.
+ * </p>
+ *
+ * <p>
+ * A negative startIndex is treated as zero. A startIndex larger than the
array length will return {@code -1}.
+ * </p>
+ *
+ * @param array the array to search through for the object, may be
{@code null}
+ * @param valueToFind the value to find
+ * @param startIndex the index to start searching at
+ * @param len the number of elements to search from the start
index
+ * @return the index of the value within the array, {@code -1}
if not found or {@code null} array input
+ * or non-positive number of elements
+ */
+ public static int indexOf(byte[] array, byte valueToFind, int startIndex,
int len) {
+ if (array == null) {
+ return -1;
+ }
+
+ for (int i = Math.max(startIndex, 0), l = 0; l < len; i++, l++) {
+ if (valueToFind == array[i]) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
public static void dumpHex(
SimplifiedLog logger, Level level, String prefix, PropertyResolver
resolver, char sep, byte... data) {
dumpHex(logger, level, prefix, resolver, sep, data, 0,
NumberUtils.length(data));
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LineDataConsumer.java
similarity index 64%
copy from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
copy to
sshd-common/src/main/java/org/apache/sshd/common/util/io/LineDataConsumer.java
index 9c8b218..a58e690 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LineDataConsumer.java
@@ -19,28 +19,28 @@
package org.apache.sshd.common.util.io;
-import java.io.FilterReader;
import java.io.IOException;
-import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.util.Objects;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
*/
-public class NoCloseReader extends FilterReader {
- public NoCloseReader(Reader in) {
- super(in);
- }
+@FunctionalInterface
+public interface LineDataConsumer {
+ /**
+ * Ignores anything provided to it
+ */
+ LineDataConsumer IGNORE = lineData -> {
+ // do nothing
+ };
- @Override
- public void close() throws IOException {
- // ignored
- }
+ /**
+ * Throws {@link StreamCorruptedException} with the invoked line data
+ */
+ LineDataConsumer FAIL = lineData -> {
+ throw new StreamCorruptedException(Objects.toString(lineData));
+ };
- public static Reader resolveReader(Reader r, boolean okToClose) {
- if ((r == null) || okToClose) {
- return r;
- } else {
- return new NoCloseReader(r);
- }
- }
+ void consume(CharSequence lineData) throws IOException;
}
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/CloseableEmptyInputStream.java
similarity index 98%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/CloseableEmptyInputStream.java
index c3c2209..5eb6aba 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/CloseableEmptyInputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.IOException;
import java.nio.channels.Channel;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/EmptyInputStream.java
similarity index 97%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/EmptyInputStream.java
index 4282adb..5674326 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/EmptyInputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.IOException;
import java.io.InputStream;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/InputStreamWithChannel.java
similarity index 96%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/InputStreamWithChannel.java
index d847079..5cd1a5d 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/InputStreamWithChannel.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.InputStream;
import java.nio.channels.Channel;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/LimitInputStream.java
similarity index 98%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/LimitInputStream.java
index 1d5b33b..3cd9956 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/LimitInputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.FilterInputStream;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseInputStream.java
similarity index 96%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseInputStream.java
index b9aedee..0cf9b04 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseInputStream.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.FilterInputStream;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseReader.java
similarity index 96%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseReader.java
index 9c8b218..a85e6d4 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseReader.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.FilterReader;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NullInputStream.java
similarity index 98%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NullInputStream.java
index 1b9b471..fc949ec 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NullInputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.EOFException;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppender.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppender.java
new file mode 100644
index 0000000..df8c7e7
--- /dev/null
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppender.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io.output;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.function.BooleanSupplier;
+
+import org.apache.sshd.common.util.io.LineDataConsumer;
+
+/**
+ * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
+ */
+public interface LineLevelAppender extends LineDataConsumer, Closeable {
+ /**
+ * A typical line length used in many textual standards
+ */
+ int TYPICAL_LINE_LENGTH = 80;
+
+ LineLevelAppender EMPTY = new LineLevelAppender() {
+ @Override
+ public void writeLineData(CharSequence lineData) throws IOException {
+ // ignored
+ }
+
+ @Override
+ public boolean isWriteEnabled() {
+ return false;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // do nothing
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * @return {@code true} if OK to accumulate data in work buffer
+ */
+ boolean isWriteEnabled();
+
+ @Override
+ default void consume(CharSequence lineData) throws IOException {
+ writeLineData(lineData);
+ }
+
+ /**
+ * Called by the implementation once end-of-line is detected.
+ *
+ * @param lineData The "pure" line data - excluding any
CR/LF(s).
+ * @throws IOException If failed to write the data
+ */
+ void writeLineData(CharSequence lineData) throws IOException;
+
+ static LineLevelAppender wrap(Appendable appendable) {
+ return wrap(appendable, () -> true);
+ }
+
+ static LineLevelAppender wrap(Appendable appendable, BooleanSupplier
writeEnabled) {
+ Objects.requireNonNull(appendable, "No appendable to wrap");
+ return new LineLevelAppender() {
+ /**
+ * indicates whether a line has been written
+ */
+ private boolean writtenFirstLine;
+
+ @Override
+ public void close() throws IOException {
+ if (appendable instanceof Closeable) {
+ ((Closeable) appendable).close();
+ }
+ }
+
+ @Override
+ public void writeLineData(CharSequence lineData) throws
IOException {
+ if (writtenFirstLine) {
+ appendable.append(System.lineSeparator());
+ }
+
+ appendable.append(lineData);
+ writtenFirstLine = true;
+ }
+
+ @Override
+ public boolean isWriteEnabled() {
+ return writeEnabled.getAsBoolean();
+ }
+
+ @Override
+ public String toString() {
+ return appendable.toString();
+ }
+ };
+ }
+}
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppenderStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppenderStream.java
new file mode 100644
index 0000000..0270cce
--- /dev/null
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppenderStream.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io.output;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * <P>
+ * Accumulates all written data into a work buffer and calls the actual
writing method only when LF detected.
+ * <B>Note:</B> it strips CR if found before the LF
+ * </P>
+ *
+ * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
+ */
+public class LineLevelAppenderStream extends LineOutputStream {
+ protected final CharsetDecoder csDecoder;
+ protected final LineLevelAppender appenderInstance;
+ protected char[] lineBuf;
+
+ public LineLevelAppenderStream(LineLevelAppender appender) {
+ this(Charset.defaultCharset(), appender);
+ }
+
+ public LineLevelAppenderStream(String charset, LineLevelAppender appender)
{
+ this(Charset.forName(ValidateUtils.checkNotNullAndNotEmpty(charset,
"No charset name")), appender);
+ }
+
+ public LineLevelAppenderStream(Charset charset, LineLevelAppender
appender) {
+ this(Objects.requireNonNull(charset, "No charset").newDecoder(),
appender);
+ }
+
+ public LineLevelAppenderStream(CharsetDecoder decoder, LineLevelAppender
appender) {
+ csDecoder = Objects.requireNonNull(decoder, "No decoder");
+ appenderInstance = Objects.requireNonNull(appender, "No appender");
+ }
+
+ public final LineLevelAppender getLineLevelAppender() {
+ return appenderInstance;
+ }
+
+ @Override
+ protected void handleLine(byte[] b, int off, int len) throws IOException {
+ LineLevelAppender appender = getLineLevelAppender();
+ if (len <= 0) {
+ appender.writeLineData("");
+ return;
+ }
+
+ ByteBuffer bb = (b[off + len - 1] == '\r') ? ByteBuffer.wrap(b, off,
len - 1) : ByteBuffer.wrap(b, off, len);
+ char[] lineChars = ensureCharDataCapacity(len);
+ CharBuffer cc = CharBuffer.wrap(lineChars);
+
+ csDecoder.reset();
+ CoderResult res = csDecoder.decode(bb, cc, true);
+ if (res.isError() || res.isMalformed() || res.isOverflow() ||
res.isUnmappable()) {
+ throw new StreamCorruptedException("Failed to decode line bytes: "
+ res);
+ }
+
+ cc.flip();
+ appender.writeLineData(cc);
+ }
+
+ protected char[] ensureCharDataCapacity(int numBytes) {
+ float grwFactor = csDecoder.maxCharsPerByte(); // worst case
+ int reqChars = (grwFactor > 0.0f) ? (int) (numBytes * grwFactor) :
numBytes;
+ if ((lineBuf == null) || (lineBuf.length < reqChars)) {
+ reqChars = Math.max(reqChars,
LineLevelAppender.TYPICAL_LINE_LENGTH);
+ lineBuf = new char[reqChars + Byte.SIZE /* a little extra to avoid
numerous growths */];
+ }
+
+ return lineBuf;
+ }
+}
diff --git
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/common/util/io/LineOutputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineOutputStream.java
similarity index 98%
rename from
sshd-contrib/src/main/java/org/apache/sshd/contrib/common/util/io/LineOutputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineOutputStream.java
index b5085b1..b093d1e 100644
---
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/common/util/io/LineOutputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineOutputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.contrib.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.IOException;
import java.io.OutputStream;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LoggingFilterOutputStream.java
similarity index 98%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LoggingFilterOutputStream.java
index b446de2..4511c88 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LoggingFilterOutputStream.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.FilterOutputStream;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseOutputStream.java
similarity index 96%
copy from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
copy to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseOutputStream.java
index 4ba16d3..ae57f3c 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseOutputStream.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.FilterOutputStream;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseWriter.java
similarity index 96%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseWriter.java
index 0f92697..a2378cf 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseWriter.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.FilterWriter;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullOutputStream.java
similarity index 97%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullOutputStream.java
index 8a0435c..64ff73e 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullOutputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.EOFException;
import java.io.IOException;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullPrintStream.java
similarity index 98%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullPrintStream.java
index a21c5d2..7466a1f 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullPrintStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.PrintStream;
import java.util.Locale;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/OutputStreamWithChannel.java
similarity index 95%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/OutputStreamWithChannel.java
index 6f81872..23deb7c 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/OutputStreamWithChannel.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.OutputStream;
import java.nio.channels.Channel;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/SecureByteArrayOutputStream.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/SecureByteArrayOutputStream.java
similarity index 97%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/SecureByteArrayOutputStream.java
rename to
sshd-common/src/main/java/org/apache/sshd/common/util/io/output/SecureByteArrayOutputStream.java
index 3cd073f..a6193e9 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/SecureByteArrayOutputStream.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/SecureByteArrayOutputStream.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java
index 054111e..83f1775 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java
@@ -43,10 +43,10 @@ import
org.apache.sshd.common.config.keys.FilePasswordProvider;
import
org.apache.sshd.common.config.keys.loader.pem.AbstractPEMResourceKeyPairParser;
import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
import org.apache.sshd.common.util.io.der.ASN1Object;
import org.apache.sshd.common.util.io.der.ASN1Type;
import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
index b4cd0f6..478f97d 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
+++
b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
@@ -43,7 +43,7 @@ import
org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
/**
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java
index 83f345c..f042f8d 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java
@@ -45,7 +45,7 @@ import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
-import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
+import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream;
import org.apache.sshd.common.util.io.resource.PathResource;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/EmptyInputStreamTest.java
similarity index 99%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/input/EmptyInputStreamTest.java
index 113758d..2958f0e 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/EmptyInputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.IOException;
import java.io.InputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/LimitInputStreamTest.java
similarity index 99%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/input/LimitInputStreamTest.java
index e009527..4bd71db 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/LimitInputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.IOException;
import java.io.InputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseInputStreamTest.java
similarity index 98%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseInputStreamTest.java
index 33ab102..e22a2ba 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseInputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.IOException;
import java.io.InputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseReaderTest.java
similarity index 98%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseReaderTest.java
index 14a72fc..471a012 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseReaderTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.IOException;
import java.io.InputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NullInputStreamTest.java
similarity index 98%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NullInputStreamTest.java
index 31982da..0e8ee80 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NullInputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.input;
import java.io.EOFException;
import java.io.IOException;
diff --git
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/common/util/io/LineOutputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/LineOutputStreamTest.java
similarity index 98%
rename from
sshd-contrib/src/test/java/org/apache/sshd/contrib/common/util/io/LineOutputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/output/LineOutputStreamTest.java
index 7e58da1..54b2259 100644
---
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/common/util/io/LineOutputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/LineOutputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.contrib.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.IOException;
import java.io.InputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseOutputStreamTest.java
similarity index 98%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseOutputStreamTest.java
index f01f132..60db28b 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseOutputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.IOException;
import java.io.OutputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseWriterTest.java
similarity index 98%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseWriterTest.java
index 090ac15..41cb5df 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseWriterTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.IOException;
import java.io.OutputStream;
diff --git
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NullOutputStreamTest.java
similarity index 98%
rename from
sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
rename to
sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NullOutputStreamTest.java
index 0fe845f..0a8872e 100644
---
a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
+++
b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NullOutputStreamTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.sshd.common.util.io;
+package org.apache.sshd.common.util.io.output;
import java.io.EOFException;
import java.io.IOException;
diff --git
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java
index 735c507..fa35b84 100644
---
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java
+++
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java
@@ -52,7 +52,7 @@ import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.contrib.common.io.EndlessWriteFuture;
import org.apache.sshd.contrib.common.io.ImmediateWriteFuture;
diff --git
a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index ff4c2f6..4700198 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -56,8 +56,8 @@ import org.apache.sshd.common.forward.PortForwardingManager;
import org.apache.sshd.common.future.KeyExchangeFuture;
import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
-import org.apache.sshd.common.util.io.NullOutputStream;
+import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.output.NullOutputStream;
import org.apache.sshd.common.util.net.SshdSocketAddress;
/**
diff --git
a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index 963a14b..84b8b72 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -60,7 +60,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.closeable.IoBaseCloseable;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.LoggingFilterOutputStream;
+import org.apache.sshd.common.util.io.output.LoggingFilterOutputStream;
import org.apache.sshd.core.CoreModuleProperties;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ServerFactoryManager;
diff --git
a/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
b/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
index cc7118d..d7f167f 100644
---
a/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
+++
b/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
@@ -26,7 +26,7 @@ import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.channel.ChannelPipedInputStream;
import org.apache.sshd.common.channel.ChannelPipedOutputStream;
import org.apache.sshd.common.channel.Window;
-import org.apache.sshd.common.util.io.LoggingFilterOutputStream;
+import org.apache.sshd.common.util.io.output.LoggingFilterOutputStream;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
/**
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
index a536252..874c5b1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
@@ -52,7 +52,7 @@ import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ProxyUtils;
-import org.apache.sshd.common.util.io.NullOutputStream;
+import org.apache.sshd.common.util.io.output.NullOutputStream;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.core.CoreModuleProperties;
import org.apache.sshd.server.Environment;
diff --git a/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java
b/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java
index bb43718..a7e2a70 100644
--- a/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java
@@ -41,7 +41,7 @@ import org.apache.sshd.common.io.WritePendingException;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.server.Environment;
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
index cf2e0cd..b1ae9b1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
@@ -90,7 +90,7 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.core.CoreModuleProperties;
diff --git
a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java
b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java
index bd8a715..b6df091 100644
---
a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java
+++
b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java
@@ -37,8 +37,8 @@ import java.util.Objects;
import org.apache.sshd.common.cipher.ECCurves;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.io.input.NoCloseInputStream;
+import org.apache.sshd.common.util.io.input.NoCloseReader;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
import org.apache.sshd.util.test.BaseTestSupport;
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 fc2a036..3d87921 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,7 +35,7 @@ import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.util.SelectorUtils;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.LimitInputStream;
+import org.apache.sshd.common.util.io.input.LimitInputStream;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.scp.ScpModuleProperties;
import org.apache.sshd.scp.client.ScpClient.Option;
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 d560f6f..d713b43 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
@@ -44,7 +44,7 @@ import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.session.SessionHolder;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.LimitInputStream;
+import org.apache.sshd.common.util.io.input.LimitInputStream;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.scp.ScpModuleProperties;
import org.apache.sshd.scp.common.ScpTransferEventListener.FileOperation;
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java
index 84514d2..986085b 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java
@@ -65,7 +65,34 @@ public interface SftpClientFactory {
* @return The created {@link SftpClient} instance
* @throws IOException If failed to create the client
*/
- SftpClient createSftpClient(ClientSession session, SftpVersionSelector
selector) throws IOException;
+ default SftpClient createSftpClient(ClientSession session,
SftpVersionSelector selector) throws IOException {
+ return createSftpClient(session, selector, SftpErrorDataHandler.EMPTY);
+ }
+
+ /**
+ * Create an SFTP client from this session.
+ *
+ * @param session The {@link ClientSession} to be used for
creating the SFTP client
+ * @param errorDataHandler The {@link SftpErrorDataHandler} to handle
incoming data through the error stream - if
+ * {@code null} the data is silently ignored
+ * @return The created {@link SftpClient}
+ * @throws IOException if failed to create the client
+ */
+ default SftpClient createSftpClient(ClientSession session,
SftpErrorDataHandler errorDataHandler) throws IOException {
+ return createSftpClient(session, SftpVersionSelector.CURRENT,
errorDataHandler);
+ }
+
+ /**
+ * @param session The {@link ClientSession} to which the SFTP
client should be attached
+ * @param selector The {@link SftpVersionSelector} to use in
order to negotiate the SFTP version
+ * @param errorDataHandler The {@link SftpErrorDataHandler} to handle
incoming data through the error stream - if
+ * {@code null} the data is silently ignored
+ * @return The created {@link SftpClient} instance
+ * @throws IOException If failed to create the client
+ */
+ SftpClient createSftpClient(
+ ClientSession session, SftpVersionSelector selector,
SftpErrorDataHandler errorDataHandler)
+ throws IOException;
default SftpFileSystem createSftpFileSystem(ClientSession session) throws
IOException {
return createSftpFileSystem(session, SftpVersionSelector.CURRENT);
@@ -90,16 +117,25 @@ public interface SftpClientFactory {
return createSftpFileSystem(session, SftpVersionSelector.CURRENT,
readBufferSize, writeBufferSize);
}
+ default SftpFileSystem createSftpFileSystem(
+ ClientSession session, SftpVersionSelector selector, int
readBufferSize, int writeBufferSize)
+ throws IOException {
+ return createSftpFileSystem(session, selector,
SftpErrorDataHandler.EMPTY, readBufferSize, writeBufferSize);
+ }
+
/**
- * @param session The {@link ClientSession} to which the SFTP
client backing the file system should be
- * attached
- * @param selector The {@link SftpVersionSelector} to use in order
to negotiate the SFTP version
- * @param readBufferSize Default I/O read buffer size
- * @param writeBufferSize Default I/O write buffer size
- * @return The created {@link SftpFileSystem} instance
- * @throws IOException If failed to create the instance
+ * @param session The {@link ClientSession} to which the SFTP
client backing the file system should be
+ * attached
+ * @param selector The {@link SftpVersionSelector} to use in
order to negotiate the SFTP version
+ * @param errorDataHandler The {@link SftpErrorDataHandler} to handle
incoming data through the error stream - if
+ * {@code null} the data is silently ignored
+ * @param readBufferSize Default I/O read buffer size
+ * @param writeBufferSize Default I/O write buffer size
+ * @return The created {@link SftpFileSystem} instance
+ * @throws IOException If failed to create the instance
*/
SftpFileSystem createSftpFileSystem(
- ClientSession session, SftpVersionSelector selector, int
readBufferSize, int writeBufferSize)
+ ClientSession session, SftpVersionSelector selector,
SftpErrorDataHandler errorDataHandler,
+ int readBufferSize, int writeBufferSize)
throws IOException;
}
diff --git
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpErrorDataHandler.java
similarity index 60%
rename from
sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
rename to
sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpErrorDataHandler.java
index 4ba16d3..76edf2a 100644
---
a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpErrorDataHandler.java
@@ -16,32 +16,28 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sshd.common.util.io;
-import java.io.FilterOutputStream;
+package org.apache.sshd.sftp.client;
+
import java.io.IOException;
-import java.io.OutputStream;
/**
- * TODO Add javadoc
+ * Callback for any error stream data sent by the server
*
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
*/
-public class NoCloseOutputStream extends FilterOutputStream {
- public NoCloseOutputStream(OutputStream out) {
- super(out);
- }
-
- @Override
- public void close() throws IOException {
- // ignored
- }
+@FunctionalInterface
+public interface SftpErrorDataHandler {
+ SftpErrorDataHandler EMPTY = (buf, start, len) -> {
+ /* ignored */ };
- public static OutputStream resolveOutputStream(OutputStream output,
boolean okToClose) {
- if ((output == null) || okToClose) {
- return output;
- } else {
- return new NoCloseOutputStream(output);
- }
- }
+ /**
+ * Receive binary data from server error stream
+ *
+ * @param buf The buffer of the incoming data
+ * @param start Offset in buffer to read the data
+ * @param len Available data in buffer
+ * @throws IOException If failed to receive incoming data
+ */
+ void errorData(byte[] buf, int start, int len) throws IOException;
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java
index f962796..c29c333 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java
@@ -52,6 +52,7 @@ import org.apache.sshd.sftp.SftpModuleProperties;
import org.apache.sshd.sftp.client.RawSftpClient;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClientFactory;
+import org.apache.sshd.sftp.client.SftpErrorDataHandler;
import org.apache.sshd.sftp.client.SftpVersionSelector;
import org.apache.sshd.sftp.client.impl.AbstractSftpClient;
import org.apache.sshd.sftp.common.SftpConstants;
@@ -67,6 +68,7 @@ public class SftpFileSystem
private final ClientSession clientSession;
private final SftpClientFactory factory;
private final SftpVersionSelector selector;
+ private final SftpErrorDataHandler errorDataHandler;
private final Queue<SftpClient> pool;
private final ThreadLocal<Wrapper> wrappers = new ThreadLocal<>();
private final int version;
@@ -77,12 +79,14 @@ public class SftpFileSystem
private final List<FileStore> stores;
public SftpFileSystem(SftpFileSystemProvider provider, String id,
ClientSession session,
- SftpClientFactory factory, SftpVersionSelector
selector) throws IOException {
+ SftpClientFactory factory, SftpVersionSelector
selector, SftpErrorDataHandler errorDataHandler)
+
throws IOException {
super(provider);
this.id = id;
this.clientSession = Objects.requireNonNull(session, "No client
session");
this.factory = factory != null ? factory :
SftpClientFactory.instance();
this.selector = selector;
+ this.errorDataHandler = errorDataHandler;
this.stores = Collections.unmodifiableList(Collections.<FileStore>
singletonList(new SftpFileStore(id, this)));
this.pool = new
LinkedBlockingQueue<>(SftpModuleProperties.POOL_SIZE.getRequired(session));
try (SftpClient client = getClient()) {
@@ -104,6 +108,10 @@ public class SftpFileSystem
return selector;
}
+ public SftpErrorDataHandler getSftpErrorDataHandler() {
+ return errorDataHandler;
+ }
+
public final String getId() {
return id;
}
@@ -171,10 +179,13 @@ public class SftpFileSystem
SftpClient client = pool.poll();
if (client == null) {
ClientSession session = getClientSession();
- client = factory.createSftpClient(session,
getSftpVersionSelector());
+ client = factory.createSftpClient(
+ session, getSftpVersionSelector(),
getSftpErrorDataHandler());
}
if (!client.isClosing()) {
- wrapper = new Wrapper(client, getReadBufferSize(),
getWriteBufferSize());
+ wrapper = new Wrapper(
+ client,
+ getSftpErrorDataHandler(), getReadBufferSize(),
getWriteBufferSize());
}
}
wrappers.set(wrapper);
@@ -231,7 +242,9 @@ public class SftpFileSystem
private final int readSize;
private final int writeSize;
- private Wrapper(SftpClient delegate, int readSize, int writeSize) {
+ private Wrapper(SftpClient delegate, SftpErrorDataHandler
errorHandler, int readSize, int writeSize) {
+ super(errorHandler);
+
this.delegate = delegate;
this.readSize = readSize;
this.writeSize = writeSize;
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java
index fa27dd4..890864e 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java
@@ -26,6 +26,7 @@ import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.session.ClientSessionCreator;
import org.apache.sshd.common.auth.PasswordHolder;
import org.apache.sshd.common.auth.UsernameHolder;
+import org.apache.sshd.sftp.client.SftpErrorDataHandler;
import org.apache.sshd.sftp.client.SftpVersionSelector;
/**
@@ -86,17 +87,20 @@ public interface SftpFileSystemClientSessionInitializer {
* Invoked by the {@link
SftpFileSystemProvider#newFileSystem(java.net.URI, Map)} method in order to
create the
* {@link SftpFileSystem} once session has been authenticated.
*
- * @param provider The {@link SftpFileSystemProvider} instance
requesting the session
- * @param context The initialization {@link
SftpFileSystemInitializationContext}
- * @param session The authenticated {@link ClientSession}
- * @param selector The <U>resolved</U> {@link SftpVersionSelector} to
use
- * @return The created {@link SftpFileSystem}
- * @throws IOException If failed to create the file-system
+ * @param provider The {@link SftpFileSystemProvider} instance
requesting the session
+ * @param context The initialization {@link
SftpFileSystemInitializationContext}
+ * @param session The authenticated {@link ClientSession}
+ * @param selector The <U>resolved</U> {@link
SftpVersionSelector} to use
+ * @param errorDataHandler The {@link SftpErrorDataHandler} to handle
incoming data through the error stream - if
+ * {@code null} the data is silently ignored
+ * @return The created {@link SftpFileSystem}
+ * @throws IOException If failed to create the file-system
*/
default SftpFileSystem createSftpFileSystem(
SftpFileSystemProvider provider,
SftpFileSystemInitializationContext context, ClientSession session,
- SftpVersionSelector selector)
+ SftpVersionSelector selector, SftpErrorDataHandler
errorDataHandler)
throws IOException {
- return new SftpFileSystem(provider, context.getId(), session,
provider.getSftpClientFactory(), selector);
+ return new SftpFileSystem(
+ provider, context.getId(), session,
provider.getSftpClientFactory(), selector, errorDataHandler);
}
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
index c5e0f0d..e671298 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java
@@ -91,6 +91,7 @@ import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClient.Attributes;
import org.apache.sshd.sftp.client.SftpClient.OpenMode;
import org.apache.sshd.sftp.client.SftpClientFactory;
+import org.apache.sshd.sftp.client.SftpErrorDataHandler;
import org.apache.sshd.sftp.client.SftpVersionSelector;
import org.apache.sshd.sftp.client.extensions.CopyFileExtension;
import org.apache.sshd.sftp.client.impl.SftpRemotePathChannel;
@@ -132,6 +133,7 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
private final SshClient clientInstance;
private final SftpClientFactory factory;
private final SftpVersionSelector versionSelector;
+ private final SftpErrorDataHandler errorDataHandler;
private final NavigableMap<String, SftpFileSystem> fileSystems = new
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private SftpFileSystemClientSessionInitializer fsSessionInitializer =
SftpFileSystemClientSessionInitializer.DEFAULT;
@@ -143,23 +145,38 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
this(null, selector);
}
- /**
- * @param client The {@link SshClient} to use - if {@code null} then a
default one will be setup and started.
- * Otherwise, it is assumed that the client has already been
started
- * @see SshClient#setUpDefaultClient()
- */
public SftpFileSystemProvider(SshClient client) {
this(client, SftpVersionSelector.CURRENT);
}
public SftpFileSystemProvider(SshClient client, SftpVersionSelector
selector) {
- this(client, null, selector);
+ this(client, selector, SftpErrorDataHandler.EMPTY);
+ }
+
+ public SftpFileSystemProvider(SshClient client, SftpVersionSelector
selector, SftpErrorDataHandler errorDataHandler) {
+ this(client, null, selector, errorDataHandler);
+
}
public SftpFileSystemProvider(SshClient client, SftpClientFactory factory,
SftpVersionSelector selector) {
+ this(client, factory, selector, SftpErrorDataHandler.EMPTY);
+ }
+
+ /**
+ * @param client The {@link SshClient} to use - if {@code null}
then a default one will be setup and
+ * started. Otherwise, it is assumed that the
client has already been started
+ * @param factory The {@link SftpClientFactory} to use to
generate SFTP client instances
+ * @param selector The {@link SftpVersionSelector} to use in order
to negotiate the SFTP version
+ * @param errorDataHandler The {@link SftpErrorDataHandler} to handle
incoming data through the error stream - if
+ * {@code null} the data is silently ignored
+ * @see SshClient#setUpDefaultClient()
+ */
+ public SftpFileSystemProvider(SshClient client, SftpClientFactory factory,
+ SftpVersionSelector selector,
SftpErrorDataHandler errorDataHandler) {
this.log = LoggerFactory.getLogger(getClass());
this.factory = factory;
this.versionSelector = selector;
+ this.errorDataHandler = errorDataHandler;
if (client == null) {
// TODO: make this configurable using system properties
client = SshClient.setUpDefaultClient();
@@ -177,6 +194,10 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
return versionSelector;
}
+ public SftpErrorDataHandler getSftpErrorDataHandler() {
+ return errorDataHandler;
+ }
+
public final SshClient getClientInstance() {
return clientInstance;
}
@@ -218,6 +239,7 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
context.setMaxAuthTime(SftpModuleProperties.AUTH_TIME.getRequired(resolver));
SftpVersionSelector selector = resolveSftpVersionSelector(uri,
getSftpVersionSelector(), resolver);
+ SftpErrorDataHandler errorHandler = resolveSftpErrorDataHandler(uri,
getSftpErrorDataHandler(), resolver);
Charset decodingCharset =
SftpModuleProperties.NAME_DECODER_CHARSET.getRequired(resolver);
SftpFileSystemClientSessionInitializer initializer =
getSftpFileSystemClientSessionInitializer();
@@ -250,7 +272,8 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
initializer.authenticateClientSession(this, context, session);
- fileSystem = initializer.createSftpFileSystem(this, context,
session, selector);
+ fileSystem = initializer.createSftpFileSystem(
+ this, context, session, selector, errorHandler);
fileSystems.put(id, fileSystem);
} catch (Exception e) {
if (session != null) {
@@ -311,6 +334,11 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
}
}
+ protected SftpErrorDataHandler resolveSftpErrorDataHandler(
+ URI uri, SftpErrorDataHandler errorHandler, PropertyResolver
resolver) {
+ return errorHandler;
+ }
+
// NOTE: URI parameters override environment ones
public static Map<String, Object> resolveFileSystemParameters(Map<String,
?> env, Map<String, Object> uriParams) {
if (MapEntryUtils.isEmpty(env)) {
@@ -395,7 +423,7 @@ public class SftpFileSystemProvider extends
FileSystemProvider {
if (fileSystems.containsKey(id)) {
throw new FileSystemAlreadyExistsException(id);
}
- fileSystem = new SftpFileSystem(this, id, session, factory,
getSftpVersionSelector());
+ fileSystem = new SftpFileSystem(this, id, session, factory,
getSftpVersionSelector(), getSftpErrorDataHandler());
fileSystems.put(id, fileSystem);
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java
index 0d29b51..7a3ce00 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java
@@ -45,6 +45,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.sftp.SftpModuleProperties;
import org.apache.sshd.sftp.client.FullAccessSftpClient;
+import org.apache.sshd.sftp.client.SftpErrorDataHandler;
import org.apache.sshd.sftp.client.extensions.BuiltinSftpClientExtensions;
import org.apache.sshd.sftp.client.extensions.SftpClientExtension;
import org.apache.sshd.sftp.client.extensions.SftpClientExtensionFactory;
@@ -56,13 +57,18 @@ import org.apache.sshd.sftp.common.extensions.ParserUtils;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
*/
-public abstract class AbstractSftpClient extends AbstractSubsystemClient
implements FullAccessSftpClient {
+public abstract class AbstractSftpClient
+ extends AbstractSubsystemClient
+ implements FullAccessSftpClient, SftpErrorDataHandler {
public static final int INIT_COMMAND_SIZE = Byte.BYTES /* command */ +
Integer.BYTES /* version */;
+ protected final SftpErrorDataHandler errorDataHandler;
+
private final Attributes fileOpenAttributes = new Attributes();
private final AtomicReference<Map<String, Object>> parsedExtensionsHolder
= new AtomicReference<>(null);
- protected AbstractSftpClient() {
+ protected AbstractSftpClient(SftpErrorDataHandler delegateHandler) {
+ errorDataHandler = (delegateHandler == null) ?
SftpErrorDataHandler.EMPTY : delegateHandler;
fileOpenAttributes.setType(SftpConstants.SSH_FILEXFER_TYPE_REGULAR);
}
@@ -846,6 +852,17 @@ public abstract class AbstractSftpClient extends
AbstractSubsystemClient impleme
}
@Override
+ public void errorData(byte[] buf, int start, int len) throws IOException {
+ /*
+ * The protocol does not specify how to handle such data but we are
lenient and ignore it - similar to
+ * /dev/null
+ */
+ if (errorDataHandler != null) {
+ errorDataHandler.errorData(buf, start, len);
+ }
+ }
+
+ @Override
public void write(Handle handle, long fileOffset, byte[] src, int
srcOffset, int len) throws IOException {
// do some bounds checking first
if ((fileOffset < 0L) || (srcOffset < 0) || (len < 0)) {
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java
index ae3f1c7..21f21ac 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java
@@ -56,9 +56,9 @@ import org.apache.sshd.common.util.MapEntryUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.NullOutputStream;
import org.apache.sshd.core.CoreModuleProperties;
import org.apache.sshd.sftp.SftpModuleProperties;
+import org.apache.sshd.sftp.client.SftpErrorDataHandler;
import org.apache.sshd.sftp.client.SftpVersionSelector;
import org.apache.sshd.sftp.common.SftpConstants;
import org.apache.sshd.sftp.common.extensions.ParserUtils;
@@ -84,9 +84,16 @@ public class DefaultSftpClient extends AbstractSftpClient {
* @param clientSession The {@link ClientSession}
* @param initialVersionSelector The initial {@link SftpVersionSelector}
- if {@code null} then version 6 is
* assumed.
+ * @param errorDataHandler The {@link SftpErrorDataHandler} to
handle incoming data through the error stream
+ * - if {@code null} the data is silently
ignored
* @throws IOException If failed to initialize
*/
- public DefaultSftpClient(ClientSession clientSession, SftpVersionSelector
initialVersionSelector) throws IOException {
+ public DefaultSftpClient(
+ ClientSession clientSession, SftpVersionSelector
initialVersionSelector,
+ SftpErrorDataHandler errorDataHandler)
+ throws
IOException {
+ super(errorDataHandler);
+
this.nameDecodingCharset =
SftpModuleProperties.NAME_DECODING_CHARSET.getRequired(clientSession);
this.clientSession = Objects.requireNonNull(clientSession, "No client
session");
this.channel = createSftpChannelSubsystem(clientSession);
@@ -161,11 +168,11 @@ public class DefaultSftpClient extends AbstractSftpClient
{
}
/**
- * Receive binary data
+ * Receive binary data from server main stream
*
- * @param buf The buffer for the incoming data
- * @param start Offset in buffer to place the data
- * @param len Available space in buffer for the data
+ * @param buf The buffer containing the incoming data
+ * @param start Offset in buffer to read the data
+ * @param len Available data in buffer
* @return Actual size of received data
* @throws IOException If failed to receive incoming data
*/
@@ -288,7 +295,8 @@ public class DefaultSftpClient extends AbstractSftpClient {
buf.putBuffer(buffer);
}
- IoOutputStream asyncIn = channel.getAsyncIn();
+ ClientChannel clientChannel = getClientChannel();
+ IoOutputStream asyncIn = clientChannel.getAsyncIn();
IoWriteFuture writeFuture = asyncIn.writeBuffer(buf);
writeFuture.verify();
return id;
@@ -364,8 +372,8 @@ public class DefaultSftpClient extends AbstractSftpClient {
buf.putInt(initialVersion);
boolean traceEnabled = log.isTraceEnabled();
- IoOutputStream asyncIn = channel.getAsyncIn();
ClientChannel clientChannel = getClientChannel();
+ IoOutputStream asyncIn = clientChannel.getAsyncIn();
if (traceEnabled) {
log.trace("init({}) send SSH_FXP_INIT - initial version={}",
clientChannel, initialVersion);
}
@@ -592,11 +600,22 @@ public class DefaultSftpClient extends AbstractSftpClient
{
}
protected OutputStream createErrOutputStream(Session session) {
- /*
- * The protocol does not specify how to handle such data but we
are lenient and ignore it - similar to
- * /dev/null
- */
- return new NullOutputStream();
+ return new OutputStream() {
+ private final byte[] singleByte = new byte[1];
+
+ @Override
+ public void write(int b) throws IOException {
+ synchronized (singleByte) {
+ singleByte[0] = (byte) b;
+ write(singleByte);
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws
IOException {
+ errorData(b, off, len);
+ }
+ };
}
}
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java
index d62f28b..11ed039 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java
@@ -26,6 +26,7 @@ import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClientFactory;
+import org.apache.sshd.sftp.client.SftpErrorDataHandler;
import org.apache.sshd.sftp.client.SftpVersionSelector;
import org.apache.sshd.sftp.client.fs.SftpFileSystem;
import org.apache.sshd.sftp.client.fs.SftpFileSystemProvider;
@@ -43,8 +44,10 @@ public class DefaultSftpClientFactory extends
AbstractLoggingBean implements Sft
}
@Override
- public SftpClient createSftpClient(ClientSession session,
SftpVersionSelector selector) throws IOException {
- DefaultSftpClient client = createDefaultSftpClient(session, selector);
+ public SftpClient createSftpClient(
+ ClientSession session, SftpVersionSelector selector,
SftpErrorDataHandler errorDataHandler)
+ throws IOException {
+ DefaultSftpClient client = createDefaultSftpClient(session, selector,
errorDataHandler);
try {
client.negotiateVersion(selector);
} catch (IOException | RuntimeException | Error e) {
@@ -57,17 +60,19 @@ public class DefaultSftpClientFactory extends
AbstractLoggingBean implements Sft
return client;
}
- protected DefaultSftpClient createDefaultSftpClient(ClientSession session,
SftpVersionSelector selector)
+ protected DefaultSftpClient createDefaultSftpClient(
+ ClientSession session, SftpVersionSelector selector,
SftpErrorDataHandler errorDataHandler)
throws IOException {
- return new DefaultSftpClient(session, selector);
+ return new DefaultSftpClient(session, selector, errorDataHandler);
}
@Override
public SftpFileSystem createSftpFileSystem(
- ClientSession session, SftpVersionSelector selector, int
readBufferSize, int writeBufferSize)
+ ClientSession session, SftpVersionSelector selector,
SftpErrorDataHandler errorDataHandler,
+ int readBufferSize, int writeBufferSize)
throws IOException {
ClientFactoryManager manager = session.getFactoryManager();
- SftpFileSystemProvider provider = new
SftpFileSystemProvider((SshClient) manager, selector);
+ SftpFileSystemProvider provider = new
SftpFileSystemProvider((SshClient) manager, selector, errorDataHandler);
SftpFileSystem fs = provider.newFileSystem(session);
if (readBufferSize > 0) {
fs.setReadBufferSize(readBufferSize);
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java
index 71d8251..3c822e8 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java
@@ -35,7 +35,7 @@ import org.apache.sshd.common.channel.Window;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.InputStreamWithChannel;
+import org.apache.sshd.common.util.io.input.InputStreamWithChannel;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClient.Attributes;
import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java
index 627d3f4..db6147e 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java
@@ -28,7 +28,7 @@ import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.OutputStreamWithChannel;
+import org.apache.sshd.common.util.io.output.OutputStreamWithChannel;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.SftpClient.OpenMode;
diff --git
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java
index e0ff3ab..27aa811 100644
---
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java
+++
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java
@@ -22,7 +22,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.Objects;
-import org.apache.sshd.common.util.io.InputStreamWithChannel;
+import org.apache.sshd.common.util.io.input.InputStreamWithChannel;
import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.SftpClient.OpenMode;
diff --git
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java
index ee4b40a..e58d25b 100644
---
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java
+++
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java
@@ -22,7 +22,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.Objects;
-import org.apache.sshd.common.util.io.OutputStreamWithChannel;
+import org.apache.sshd.common.util.io.output.OutputStreamWithChannel;
import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.SftpClient.OpenMode;