This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit ab233a4295f0e9feb21687b0e61403191d381aad
Author: Lyor Goldstein <lgoldst...@apache.org>
AuthorDate: Fri Feb 19 11:54:22 2021 +0200

    [SSHD-1127] Added capability to register a custom receiver for SFTP STDERR 
channel raw or stream data
---
 CHANGES.md                                         |  3 +-
 docs/sftp.md                                       | 22 +++++-------
 .../apache/sshd/server/channel/ChannelSession.java | 13 +++----
 .../apache/sshd/server/command/AsyncCommand.java   | 28 ++-------------
 .../command/AsyncCommandErrorStreamAware.java      | 18 ++++++----
 .../command/AsyncCommandInputStreamAware.java      | 18 ++++++----
 .../command/AsyncCommandOutputStreamAware.java     | 17 +++++----
 .../server/command/AsyncCommandStreamsAware.java   |  9 ++---
 .../org/apache/sshd/server/command/Command.java    | 27 +--------------
 .../command/CommandDirectErrorStreamAware.java     | 18 ++++++----
 .../command/CommandDirectInputStreamAware.java     | 16 +++++----
 .../command/CommandDirectOutputStreamAware.java    | 18 ++++++----
 .../server/command/CommandDirectStreamsAware.java  |  9 ++---
 ...a => SftpErrorDataChannelReceiverProvider.java} | 13 ++++---
 .../org/apache/sshd/sftp/server/SftpSubsystem.java | 40 ++++++++++++++++++++--
 .../sftp/server/SftpSubsystemConfigurator.java     |  3 +-
 .../sshd/sftp/server/SftpSubsystemFactory.java     | 18 ++++++++++
 17 files changed, 161 insertions(+), 129 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 5f33b19..685e749 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -23,6 +23,7 @@
 * [SSHD-1111](https://issues.apache.org/jira/browse/SSHD-1111) Fixed 
SshClientCliSupport compression option detection
 * [SSHD-525](https://issues.apache.org/jira/browse/SSHD-525) Added support for 
SFTP **client-side** ["posix-ren...@openssh.com"
  
extension](http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=1.28&content-type=text/x-cvsweb-markup)
 - see section 3.3
+* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Consolidated 
`SftpSubsystem` support implementations into `SftpSubsystemConfigurator`
 
 ## Behavioral changes and enhancements
 
@@ -37,4 +38,4 @@
 * [SSHD-1114](https://issues.apache.org/jira/browse/SSHD-1114) Added callbacks 
for client-side host-based authentication progress
 * [SSHD-1114](https://issues.apache.org/jira/browse/SSHD-1114) Added 
capability for interactive password authentication participation via 
UserInteraction
 * [SSHD-1114](https://issues.apache.org/jira/browse/SSHD-1114) Added 
capability for interactive key based authentication participation via 
UserInteraction
-* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Consolidated 
`SftpSubsystem` support implementations into `SftpSubsystemConfigurator`
\ No newline at end of file
+* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Added 
capability to register a custom receiver for SFTP STDERR channel raw or stream 
data
diff --git a/docs/sftp.md b/docs/sftp.md
index 1afa4c2..08707d5 100644
--- a/docs/sftp.md
+++ b/docs/sftp.md
@@ -108,23 +108,19 @@ using a registered `SftpErrorStatusDataHandler`. The 
default implementation prov
 exception type. However, users may override it when creating the 
`SftpSubsystemFactory` and provide their own codes and/or messages - e.g.,
 for debugging one can register a `DetailedSftpErrorStatusDataHandler` (see 
`sshd-contrib`) that "leaks" more information in the generated message.
 
-If the registered handler implements `ChannelSessionAware` then it will also 
be informed of the registered `ChannelSession` when it is
-provided to the `SftpSubsystem` itself. This can be used to register an 
extended data writer that can handle data sent via the STDERR
-channel. **Note:** this feature is allowed according to [SFTP version 4 - 
section 
3.1](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-3.1):
+### Intercepting data sent via STDERR channel data from the client
 
->> Packets are sent and received on stdout and stdin. Data sent on stderr by 
the server SHOULD be considered debug
->> or supplemental error information, and MAY be displayed to the user.
+If the registered handler implements `ChannelSessionAware` then it will also 
be informed of the registered `ChannelSession` when it is provided to the 
`SftpSubsystem` itself. This can be used to register an extended data writer 
that override the default (which ignores such data) and can handle data sent 
via the STDERR channel. **Note:** this feature is allowed according to [SFTP 
version 4 - section 
3.1](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-3.1):
 
-however, the current code provides no built-in support for this feature.
+>> Packets are sent and received on stdout and stdin. Data sent on stderr by 
the server SHOULD be considered  free format debug or supplemental error 
information, and MAY be displayed to the user.
 
-If registering an extended data writer then one should take care of any race 
conditions that may occur where (extended) data
-may arrive before the handler is informed of the existence of the 
`ChannelSession`. For this purpose one should configure a
-reasonable buffer size by setting the `channel-session-max-extdata-bufsize` 
property. This way, if any data arrives before the
-extended data handler is registered it will be buffered (up to the specified 
max. size). **Note:** if a buffer size is configured
-but no extended data handler is registered when channel is spawning the 
command then an exception will occur.
+however, the current code provides no built-in support for this feature other 
than ignoring any such sent data.
 
-The same applies with any error I/O streams provided to the `SftpSubsystem` - 
if the handler implements the relevant `Aware` interface
-then it will be provided with the relevant stream.
+If registering an extended data writer one should take care of any race 
conditions that may occur where (extended) data may arrive before the handler 
is informed of the existence of the `ChannelSession`. For this purpose one 
should configure a reasonable buffer size by setting the 
`channel-session-max-extdata-bufsize` property. This way, if any data arrives 
before the extended data handler is registered it will be buffered (up to the 
specified max. size). **Note:** if a buffer size is co [...]
+
+### Sending custom data via STDERR channel data to the client
+
+Same logic as the STDERR incoming data applies to the outgoing error I/O 
streams provided to the `SftpSubsystem`. If the handler implements the relevant 
`CommandDirectErrorStreamAware` and/or `AsyncCommandErrorStreamAware` interface 
then it will be provided with the relevant error stream when the SFTP subsystem 
is initialized. **Note**: the current `SftpSubsystem`implementation uses 
*asynchronous* streams so `AsyncCommandErrorStreamAware` is the interface that 
will be invoked. However, i [...]
 
 ### Symbolic links handling
 
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 1124953..cfd3dad 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
@@ -68,7 +68,8 @@ import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.SessionAware;
 import org.apache.sshd.server.Signal;
 import org.apache.sshd.server.StandardEnvironment;
-import org.apache.sshd.server.command.AsyncCommand;
+import org.apache.sshd.server.command.AsyncCommandInputStreamAware;
+import org.apache.sshd.server.command.AsyncCommandStreamsAware;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.command.CommandFactory;
 import org.apache.sshd.server.forward.AgentForwardingFilter;
@@ -719,11 +720,11 @@ public class ChannelSession extends AbstractServerChannel 
{
             ((FileSystemAware) command).setFileSystemFactory(factory, session);
         }
         // If the shell wants to use non-blocking io
-        if (command instanceof AsyncCommand) {
+        if (command instanceof AsyncCommandStreamsAware) {
             asyncOut = new ChannelAsyncOutputStream(this, 
SshConstants.SSH_MSG_CHANNEL_DATA);
             asyncErr = new ChannelAsyncOutputStream(this, 
SshConstants.SSH_MSG_CHANNEL_EXTENDED_DATA);
-            ((AsyncCommand) command).setIoOutputStream(asyncOut);
-            ((AsyncCommand) command).setIoErrorStream(asyncErr);
+            ((AsyncCommandStreamsAware) command).setIoOutputStream(asyncOut);
+            ((AsyncCommandStreamsAware) command).setIoErrorStream(asyncErr);
         } else {
             Window wRemote = getRemoteWindow();
             out = new ChannelOutputStream(
@@ -742,10 +743,10 @@ public class ChannelSession extends AbstractServerChannel 
{
         if (this.receiver == null) {
             // if the command hasn't installed any ChannelDataReceiver, 
install the default
             // and give the command an InputStream
-            if (command instanceof AsyncCommand) {
+            if (command instanceof AsyncCommandInputStreamAware) {
                 AsyncDataReceiver recv = new AsyncDataReceiver(this);
                 setDataReceiver(recv);
-                ((AsyncCommand) command).setIoInputStream(recv.getIn());
+                ((AsyncCommandInputStreamAware) 
command).setIoInputStream(recv.getIn());
             } else {
                 PipeDataReceiver recv = new PipeDataReceiver(this, 
getLocalWindow());
                 setDataReceiver(recv);
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java
index 1391735..2b411e9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java
@@ -18,34 +18,10 @@
  */
 package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.io.IoInputStream;
-import org.apache.sshd.common.io.IoOutputStream;
-
 /**
  * Represents a command capable of doing non-blocking io. If this interface is 
implemented by a command, the usual
  * blocking input / output / error streams won't be set.
  */
-public interface AsyncCommand extends Command {
-
-    /**
-     * Set the input stream that can be used by the shell to read input.
-     *
-     * @param in The {@link IoInputStream} used by the shell to read input
-     */
-    void setIoInputStream(IoInputStream in);
-
-    /**
-     * Set the output stream that can be used by the shell to write its output.
-     *
-     * @param out The {@link IoOutputStream} used by the shell to write its 
output
-     */
-    void setIoOutputStream(IoOutputStream out);
-
-    /**
-     * Set the error stream that can be used by the shell to write its errors.
-     *
-     * @param err The {@link IoOutputStream} used by the shell to write its 
errors
-     */
-    void setIoErrorStream(IoOutputStream err);
-
+public interface AsyncCommand extends Command, AsyncCommandStreamsAware {
+    // Nothing extra
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandErrorStreamAware.java
similarity index 69%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandErrorStreamAware.java
index 63dbe9c..106c47d 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandErrorStreamAware.java
@@ -17,15 +17,21 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
+package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import org.apache.sshd.common.io.IoOutputStream;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface AsyncCommandErrorStreamAware {
+
+    /**
+     * Set the error stream that can be used by the shell to write its errors.
+     *
+     * @param err The {@link IoOutputStream} used by the shell to write its 
errors
+     */
+    void setIoErrorStream(IoOutputStream err);
+
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandInputStreamAware.java
similarity index 70%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandInputStreamAware.java
index 63dbe9c..2e747a3 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandInputStreamAware.java
@@ -17,15 +17,21 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
+package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import org.apache.sshd.common.io.IoInputStream;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface AsyncCommandInputStreamAware {
+
+    /**
+     * Set the input stream that can be used by the shell to read input.
+     *
+     * @param in The {@link IoInputStream} used by the shell to read input
+     */
+    void setIoInputStream(IoInputStream in);
+
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandOutputStreamAware.java
similarity index 69%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandOutputStreamAware.java
index 63dbe9c..a443e2c 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandOutputStreamAware.java
@@ -17,15 +17,20 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
+package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import org.apache.sshd.common.io.IoOutputStream;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface AsyncCommandOutputStreamAware {
+    /**
+     * Set the output stream that can be used by the shell to write its output.
+     *
+     * @param out The {@link IoOutputStream} used by the shell to write its 
output
+     */
+    void setIoOutputStream(IoOutputStream out);
+
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandStreamsAware.java
similarity index 74%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandStreamsAware.java
index 63dbe9c..66d05cc 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandStreamsAware.java
@@ -17,15 +17,12 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
-
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+package org.apache.sshd.server.command;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
+public interface AsyncCommandStreamsAware
+        extends AsyncCommandInputStreamAware, AsyncCommandOutputStreamAware, 
AsyncCommandErrorStreamAware {
     // Nothing extra
 }
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java 
b/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java
index 16396d2..7c264ba 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java
@@ -18,9 +18,6 @@
  */
 package org.apache.sshd.server.command;
 
-import java.io.InputStream;
-import java.io.OutputStream;
-
 import org.apache.sshd.server.ExitCallback;
 
 /**
@@ -35,29 +32,7 @@ import org.apache.sshd.server.ExitCallback;
  * </p>
  * see {@link org.apache.sshd.server.shell.InvertedShellWrapper}.
  */
-public interface Command extends CommandLifecycle {
-
-    /**
-     * Set the input stream that can be used by the shell to read input.
-     *
-     * @param in The {@link InputStream} used by the shell to read input.
-     */
-    void setInputStream(InputStream in);
-
-    /**
-     * Set the output stream that can be used by the shell to write its output.
-     *
-     * @param out The {@link OutputStream} used by the shell to write its 
output
-     */
-    void setOutputStream(OutputStream out);
-
-    /**
-     * Set the error stream that can be used by the shell to write its errors.
-     *
-     * @param err The {@link OutputStream} used by the shell to write its 
errors
-     */
-    void setErrorStream(OutputStream err);
-
+public interface Command extends CommandLifecycle, CommandDirectStreamsAware {
     /**
      * Set the callback that the shell has to call when it is closed.
      *
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectErrorStreamAware.java
similarity index 70%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectErrorStreamAware.java
index 63dbe9c..7cce4af 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectErrorStreamAware.java
@@ -17,15 +17,21 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
+package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import java.io.OutputStream;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface CommandDirectErrorStreamAware {
+
+    /**
+     * Set the error stream that can be used by the shell to write its errors.
+     *
+     * @param err The {@link OutputStream} used by the shell to write its 
errors
+     */
+    void setErrorStream(OutputStream err);
+
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectInputStreamAware.java
similarity index 71%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectInputStreamAware.java
index 63dbe9c..5c9010d 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectInputStreamAware.java
@@ -17,15 +17,19 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
+package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import java.io.InputStream;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface CommandDirectInputStreamAware {
+    /**
+     * Set the input stream that can be used by the shell to read input.
+     *
+     * @param in The {@link InputStream} used by the shell to read input.
+     */
+    void setInputStream(InputStream in);
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectOutputStreamAware.java
similarity index 70%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectOutputStreamAware.java
index 63dbe9c..3dea058 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectOutputStreamAware.java
@@ -17,15 +17,21 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
+package org.apache.sshd.server.command;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import java.io.OutputStream;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface CommandDirectOutputStreamAware {
+
+    /**
+     * Set the output stream that can be used by the shell to write its output.
+     *
+     * @param out The {@link OutputStream} used by the shell to write its 
output
+     */
+    void setOutputStream(OutputStream out);
+
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectStreamsAware.java
similarity index 74%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectStreamsAware.java
index 63dbe9c..a243203 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectStreamsAware.java
@@ -17,15 +17,12 @@
  * under the License.
  */
 
-package org.apache.sshd.sftp.server;
-
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+package org.apache.sshd.server.command;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
+public interface CommandDirectStreamsAware
+        extends CommandDirectInputStreamAware, CommandDirectOutputStreamAware, 
CommandDirectErrorStreamAware {
     // Nothing extra
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpErrorDataChannelReceiverProvider.java
similarity index 70%
copy from 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
copy to 
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpErrorDataChannelReceiverProvider.java
index 63dbe9c..cabc821 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpErrorDataChannelReceiverProvider.java
@@ -19,13 +19,16 @@
 
 package org.apache.sshd.sftp.server;
 
-import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import org.apache.sshd.server.channel.ChannelDataReceiver;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public interface SftpSubsystemConfigurator
-        extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
-    // Nothing extra
+@FunctionalInterface
+public interface SftpErrorDataChannelReceiverProvider {
+    /**
+     * @return A {@link ChannelDataReceiver} to handle optional STDERR data 
received during SFTP session. If
+     *         {@code null} then any received such data is ignored.
+     */
+    ChannelDataReceiver getErrorChannelDataReceiver();
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
index 850f8e3..a5bb4ae 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
@@ -71,7 +71,9 @@ import org.apache.sshd.server.SessionAware;
 import org.apache.sshd.server.channel.ChannelDataReceiver;
 import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.AsyncCommand;
+import org.apache.sshd.server.command.AsyncCommandErrorStreamAware;
 import org.apache.sshd.server.command.Command;
+import org.apache.sshd.server.command.CommandDirectErrorStreamAware;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.sftp.SftpModuleProperties;
 import org.apache.sshd.sftp.common.SftpConstants;
@@ -96,10 +98,10 @@ public class SftpSubsystem
     protected final Map<String, Handle> handles = new ConcurrentHashMap<>();
     protected final Buffer buffer = new ByteArrayBuffer(1024);
     protected final BlockingQueue<Buffer> requests = new 
LinkedBlockingQueue<>();
+    protected final ChannelDataReceiver errorDataChannelReceiver;
 
     protected ExitCallback callback;
     protected IoOutputStream out;
-    protected IoOutputStream err;
     protected Environment env;
     protected Random randomizer;
     protected int fileHandleSize = 
SftpModuleProperties.DEFAULT_FILE_HANDLE_SIZE;
@@ -120,6 +122,31 @@ public class SftpSubsystem
     public SftpSubsystem(SftpSubsystemConfigurator configurator) {
         super(configurator);
 
+        ChannelDataReceiver receiver = 
configurator.getErrorChannelDataReceiver();
+        if (receiver == null) {
+            errorDataChannelReceiver = new ChannelDataReceiver() {
+                @Override
+                @SuppressWarnings("synthetic-access")
+                public void close() throws IOException {
+                    if (log.isDebugEnabled()) {
+                        log.debug("stderrData({}) closing", getSession());
+                    }
+
+                }
+
+                @Override
+                @SuppressWarnings("synthetic-access")
+                public int data(ChannelSession channel, byte[] buf, int start, 
int len) throws IOException {
+                    if (log.isDebugEnabled()) {
+                        log.debug("stderrData({}) received {} data bytes", 
channel, len);
+                    }
+                    return len;
+                }
+            };
+        } else {
+            errorDataChannelReceiver = receiver;
+        }
+
         CloseableExecutorService executorService = 
configurator.getExecutorService();
         if (executorService == null) {
             this.executorService = 
ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
@@ -168,6 +195,7 @@ public class SftpSubsystem
     public void setChannelSession(ChannelSession session) {
         this.channelSession = session;
         session.setDataReceiver(this);
+        session.setExtendedDataWriter(errorDataChannelReceiver);
 
         SftpErrorStatusDataHandler errHandler = getErrorStatusDataHandler();
         if (errHandler instanceof ChannelSessionAware) {
@@ -201,7 +229,10 @@ public class SftpSubsystem
 
     @Override
     public void setErrorStream(OutputStream err) {
-        // Do nothing
+        SftpErrorStatusDataHandler errHandler = getErrorStatusDataHandler();
+        if (errHandler instanceof CommandDirectErrorStreamAware) {
+            ((CommandDirectErrorStreamAware) errHandler).setErrorStream(err);
+        }
     }
 
     @Override
@@ -216,7 +247,10 @@ public class SftpSubsystem
 
     @Override
     public void setIoErrorStream(IoOutputStream err) {
-        this.err = err;
+        SftpErrorStatusDataHandler errHandler = getErrorStatusDataHandler();
+        if (errHandler instanceof AsyncCommandErrorStreamAware) {
+            ((AsyncCommandErrorStreamAware) errHandler).setIoErrorStream(err);
+        }
     }
 
     @Override
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
index 63dbe9c..09711fe 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java
@@ -26,6 +26,7 @@ import 
org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
  */
 public interface SftpSubsystemConfigurator
         extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider,
-        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider {
+        SftpUnsupportedAttributePolicyProvider, 
SftpErrorStatusDataHandlerProvider,
+        SftpErrorDataChannelReceiverProvider {
     // Nothing extra
 }
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java
index e3e0e61..444d7ca 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java
@@ -27,6 +27,7 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ObjectBuilder;
 import org.apache.sshd.common.util.threads.CloseableExecutorService;
 import org.apache.sshd.common.util.threads.ManagedExecutorServiceSupplier;
+import org.apache.sshd.server.channel.ChannelDataReceiver;
 import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.subsystem.SubsystemFactory;
@@ -49,6 +50,7 @@ public class SftpSubsystemFactory
         private UnsupportedAttributePolicy policy = DEFAULT_POLICY;
         private SftpFileSystemAccessor fileSystemAccessor = 
SftpFileSystemAccessor.DEFAULT;
         private SftpErrorStatusDataHandler errorStatusDataHandler = 
SftpErrorStatusDataHandler.DEFAULT;
+        private ChannelDataReceiver errorChannelDataReceiver;
 
         public Builder() {
             super();
@@ -74,6 +76,11 @@ public class SftpSubsystemFactory
             return this;
         }
 
+        public Builder withErrorChannelDataReceiver(ChannelDataReceiver 
receiver) {
+            errorChannelDataReceiver = receiver;
+            return this;
+        }
+
         @Override
         public SftpSubsystemFactory build() {
             SftpSubsystemFactory factory = new SftpSubsystemFactory();
@@ -81,6 +88,7 @@ public class SftpSubsystemFactory
             factory.setUnsupportedAttributePolicy(policy);
             factory.setFileSystemAccessor(fileSystemAccessor);
             factory.setErrorStatusDataHandler(errorStatusDataHandler);
+            factory.setErrorChannelDataReceiver(errorChannelDataReceiver);
             GenericUtils.forEach(getRegisteredListeners(), 
factory::addSftpEventListener);
             return factory;
         }
@@ -90,6 +98,7 @@ public class SftpSubsystemFactory
     private UnsupportedAttributePolicy policy = DEFAULT_POLICY;
     private SftpFileSystemAccessor fileSystemAccessor = 
SftpFileSystemAccessor.DEFAULT;
     private SftpErrorStatusDataHandler errorStatusDataHandler = 
SftpErrorStatusDataHandler.DEFAULT;
+    private ChannelDataReceiver errorChannelDataReceiver;
 
     public SftpSubsystemFactory() {
         super();
@@ -149,6 +158,15 @@ public class SftpSubsystemFactory
     }
 
     @Override
+    public ChannelDataReceiver getErrorChannelDataReceiver() {
+        return errorChannelDataReceiver;
+    }
+
+    public void setErrorChannelDataReceiver(ChannelDataReceiver 
errorChannelDataReceiver) {
+        this.errorChannelDataReceiver = errorChannelDataReceiver;
+    }
+
+    @Override
     public Command createSubsystem(ChannelSession channel) throws IOException {
         SftpSubsystem subsystem = new SftpSubsystem(this);
         GenericUtils.forEach(getRegisteredListeners(), 
subsystem::addSftpEventListener);

Reply via email to