You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2018/04/16 11:48:03 UTC
[16/30] mina-sshd git commit: [SSHD-815] Extract SFTP in its own
module
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
deleted file mode 100644
index d059d36..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.subsystem.sftp;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SftpConstantsTest extends BaseTestSupport {
- public SftpConstantsTest() {
- super();
- }
-
- @Test
- public void testRenameModesNotMarkedAsOpcodes() {
- for (int cmd : new int[]{
- SftpConstants.SSH_FXP_RENAME_OVERWRITE,
- SftpConstants.SSH_FXP_RENAME_ATOMIC,
- SftpConstants.SSH_FXP_RENAME_NATIVE
- }) {
- String name = SftpConstants.getCommandMessageName(cmd);
- assertFalse("Mismatched name for " + cmd + ": " + name, name.startsWith("SSH_FXP_RENAME_"));
- }
- }
-
- @Test
- public void testRealPathModesNotMarkedAsOpcodes() {
- for (int cmd = SftpConstants.SSH_FXP_REALPATH_NO_CHECK; cmd <= SftpConstants.SSH_FXP_REALPATH_STAT_IF; cmd++) {
- String name = SftpConstants.getCommandMessageName(cmd);
- assertFalse("Mismatched name for " + cmd + ": " + name, name.startsWith("SSH_FXP_REALPATH_"));
- }
- }
-
- @Test
- public void testSubstatusNameResolution() {
- for (int status = SftpConstants.SSH_FX_OK; status <= SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK; status++) {
- String name = SftpConstants.getStatusName(status);
- assertTrue("Failed to convert status=" + status + ": " + name, name.startsWith("SSH_FX_"));
- }
- }
-
- @Test
- public void testSubstatusMessageResolution() {
- for (int status = SftpConstants.SSH_FX_OK; status <= SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK; status++) {
- String message = SftpHelper.resolveStatusMessage(status);
- assertTrue("Missing message for status=" + status, GenericUtils.isNotEmpty(message));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java b/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
deleted file mode 100644
index 704aa05..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.subsystem.sftp;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SftpUniversalOwnerAndGroupTest extends BaseTestSupport {
- public SftpUniversalOwnerAndGroupTest() {
- super();
- }
-
- @Test
- public void testNameFormat() {
- for (SftpUniversalOwnerAndGroup value : SftpUniversalOwnerAndGroup.VALUES) {
- String name = value.getName();
- assertFalse(value.name() + ": empty name", GenericUtils.isEmpty(name));
- assertTrue(value.name() + ": bad suffix", name.charAt(name.length() - 1) == '@');
-
- for (int index = 0; index < name.length() - 1; index++) {
- char ch = name.charAt(index);
- if ((ch < 'A') || (ch > 'Z')) {
- fail("Non-uppercase character in " + name);
- }
- }
- }
- }
-
- @Test
- public void testFromName() {
- for (String name : new String[]{null, "", getCurrentTestName()}) {
- assertNull("Unexpected value for '" + name + "'", SftpUniversalOwnerAndGroup.fromName(name));
- }
-
- for (SftpUniversalOwnerAndGroup expected : SftpUniversalOwnerAndGroup.VALUES) {
- String name = expected.getName();
- for (int index = 0; index < name.length(); index++) {
- assertSame(name, expected, SftpUniversalOwnerAndGroup.fromName(name));
- name = shuffleCase(name);
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index 2f07cab..8c083a3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -55,7 +55,6 @@ import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.auth.UserAuthMethodFactory;
import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.channel.ChannelListener;
-import org.apache.sshd.common.channel.TestChannelListener;
import org.apache.sshd.common.channel.Window;
import org.apache.sshd.common.channel.WindowClosedException;
import org.apache.sshd.common.io.IoSession;
@@ -78,6 +77,7 @@ import org.apache.sshd.server.session.ServerSessionImpl;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.EchoShell;
import org.apache.sshd.util.test.EchoShellFactory;
+import org.apache.sshd.util.test.TestChannelListener;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
deleted file mode 100644
index 6420411..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.server.subsystem.sftp;
-
-import java.util.concurrent.ExecutorService;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SftpSubsystemFactoryTest extends BaseTestSupport {
- public SftpSubsystemFactoryTest() {
- super();
- }
-
- /**
- * Make sure that the builder returns a factory with the default values
- * if no {@code withXXX} method is invoked
- */
- @Test
- public void testBuilderDefaultFactoryValues() {
- SftpSubsystemFactory factory = new SftpSubsystemFactory.Builder().build();
- assertNull("Mismatched executor", factory.getExecutorService());
- assertFalse("Mismatched shutdown state", factory.isShutdownOnExit());
- assertSame("Mismatched unsupported attribute policy", SftpSubsystemFactory.DEFAULT_POLICY, factory.getUnsupportedAttributePolicy());
- }
-
- /**
- * Make sure that the builder initializes correctly the built factory
- */
- @Test
- public void testBuilderCorrectlyInitializesFactory() {
- SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder();
- ExecutorService service = dummyExecutor();
- SftpSubsystemFactory factory = builder.withExecutorService(service)
- .withShutdownOnExit(true)
- .build();
- assertSame("Mismatched executor", service, factory.getExecutorService());
- assertTrue("Mismatched shutdown state", factory.isShutdownOnExit());
-
- for (UnsupportedAttributePolicy policy : UnsupportedAttributePolicy.VALUES) {
- SftpSubsystemFactory actual = builder.withUnsupportedAttributePolicy(policy).build();
- assertSame("Mismatched unsupported attribute policy", policy, actual.getUnsupportedAttributePolicy());
- }
- }
-
- /**
- * <UL>
- * <LI>
- * Make sure the builder returns new instances on every call to
- * {@link SftpSubsystemFactory.Builder#build()} method
- * </LI>
- *
- * <LI>
- * Make sure values are preserved between successive invocations
- * of the {@link SftpSubsystemFactory.Builder#build()} method
- * </LI>
- * </UL
- */
- @Test
- public void testBuilderUniqueInstance() {
- SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder();
- SftpSubsystemFactory f1 = builder.withExecutorService(dummyExecutor()).build();
- SftpSubsystemFactory f2 = builder.build();
- assertNotSame("No new instance built", f1, f2);
- assertSame("Mismatched executors", f1.getExecutorService(), f2.getExecutorService());
-
- SftpSubsystemFactory f3 = builder.withExecutorService(dummyExecutor()).build();
- assertNotSame("Executor service not changed", f1.getExecutorService(), f3.getExecutorService());
- }
-
- private static ExecutorService dummyExecutor() {
- return Mockito.mock(ExecutorService.class);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
deleted file mode 100644
index e6b10e0..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.server.subsystem.sftp;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
-import org.apache.sshd.common.io.IoServiceFactory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
-import org.apache.sshd.server.SessionAware;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator;
-import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.apache.sshd.server.scp.ScpCommandFactory;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.server.shell.InteractiveProcessShellFactory;
-import org.apache.sshd.util.test.Utils;
-
-/**
- * A basic implementation to allow remote mounting of the local file system via SFTP
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class SshFsMounter {
- public static class MounterCommand extends AbstractLoggingBean implements Command, SessionAware, Runnable {
- private final String command;
- private final String cmdName;
- private final List<String> args;
- private String username;
- private InputStream stdin;
- private PrintStream stdout;
- private PrintStream stderr;
- private ExitCallback callback;
- private ExecutorService executor;
- private Future<?> future;
-
- public MounterCommand(String command) {
- this.command = ValidateUtils.checkNotNullAndNotEmpty(command, "No command");
-
- String[] comps = GenericUtils.split(this.command, ' ');
- int numComps = GenericUtils.length(comps);
- cmdName = GenericUtils.trimToEmpty(ValidateUtils.checkNotNullAndNotEmpty(comps[0], "No command name"));
- if (numComps > 1) {
- args = new ArrayList<>(numComps - 1);
- for (int index = 1; index < numComps; index++) {
- String c = GenericUtils.trimToEmpty(comps[index]);
- if (GenericUtils.isEmpty(c)) {
- continue;
- }
-
- args.add(c);
- }
- } else {
- args = Collections.emptyList();
- }
-
- log.info("<init>(" + command + ")");
- }
-
- @Override
- public void run() {
- try {
- log.info("run(" + username + ")[" + command + "] start");
- if ("id".equals(cmdName)) {
- int numArgs = GenericUtils.size(args);
- if (numArgs <= 0) {
- stdout.println("uid=0(root) gid=0(root) groups=0(root)");
- } else if (numArgs == 1) {
- String modifier = args.get(0);
- if ("-u".equals(modifier) || "-G".equals(modifier)) {
- stdout.println("0");
- } else {
- throw new IllegalArgumentException("Unknown modifier: " + modifier);
- }
- } else {
- throw new IllegalArgumentException("Unexpected extra command arguments");
- }
- } else {
- throw new UnsupportedOperationException("Unknown command");
- }
-
- log.info("run(" + username + ")[" + command + "] end");
- callback.onExit(0);
- } catch (Exception e) {
- log.error("run(" + username + ")[" + command + "] " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
- stderr.append(e.getClass().getSimpleName()).append(": ").println(e.getMessage());
- callback.onExit(-1, e.toString());
- }
- }
-
- @Override
- public void setSession(ServerSession session) {
- username = session.getUsername();
- }
-
- @Override
- public void setInputStream(InputStream in) {
- this.stdin = in;
- }
-
- @Override
- public void setOutputStream(OutputStream out) {
- this.stdout = new PrintStream(out, true);
- }
-
- @Override
- public void setErrorStream(OutputStream err) {
- this.stderr = new PrintStream(err, true);
- }
-
- @Override
- public void setExitCallback(ExitCallback callback) {
- this.callback = callback;
- }
-
- @Override
- public void start(Environment env) throws IOException {
- executor = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
- future = executor.submit(this);
- }
-
- @Override
- public void destroy() {
- stopCommand();
-
- if (stdout != null) {
- try {
- log.info("destroy(" + username + ")[" + command + "] close stdout");
- stdout.close();
- log.info("destroy(" + username + ")[" + command + "] stdout closed");
- } finally {
- stdout = null;
- }
- }
-
- if (stderr != null) {
- try {
- log.info("destroy(" + username + ")[" + command + "] close stderr");
- stderr.close();
- log.info("destroy(" + username + ")[" + command + "] stderr closed");
- } finally {
- stderr = null;
- }
- }
-
- if (stdin != null) {
- try {
- log.info("destroy(" + username + ")[" + command + "] close stdin");
- stdin.close();
- log.info("destroy(" + username + ")[" + command + "] stdin closed");
- } catch (IOException e) {
- log.warn("destroy(" + username + ")[" + command + "] failed (" + e.getClass().getSimpleName() + ") to close stdin: " + e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("destroy(" + username + ")[" + command + "] failure details", e);
- }
- } finally {
- stdin = null;
- }
- }
- }
-
- private void stopCommand() {
- if ((future != null) && (!future.isDone())) {
- try {
- log.info("stopCommand(" + username + ")[" + command + "] cancelling");
- future.cancel(true);
- log.info("stopCommand(" + username + ")[" + command + "] cancelled");
- } finally {
- future = null;
- }
- }
-
- if ((executor != null) && (!executor.isShutdown())) {
- try {
- log.info("stopCommand(" + username + ")[" + command + "] shutdown executor");
- executor.shutdownNow();
- log.info("stopCommand(" + username + ")[" + command + "] executor shut down");
- } finally {
- executor = null;
- }
- }
- }
- }
-
- public static class MounterCommandFactory implements CommandFactory {
- public static final MounterCommandFactory INSTANCE = new MounterCommandFactory();
-
- public MounterCommandFactory() {
- super();
- }
-
- @Override
- public Command createCommand(String command) {
- return new MounterCommand(command);
- }
- }
-
- private SshFsMounter() {
- throw new UnsupportedOperationException("No instance");
- }
-
- //////////////////////////////////////////////////////////////////////////
-
- public static void main(String[] args) throws Exception {
- int port = SshConfigFileReader.DEFAULT_PORT;
- boolean error = false;
- Map<String, Object> options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- int numArgs = GenericUtils.length(args);
- for (int i = 0; i < numArgs; i++) {
- String argName = args[i];
- if ("-p".equals(argName)) {
- if ((i + 1) >= numArgs) {
- System.err.println("option requires an argument: " + argName);
- break;
- }
- port = Integer.parseInt(args[++i]);
- } else if ("-io".equals(argName)) {
- if (i + 1 >= numArgs) {
- System.err.println("option requires an argument: " + argName);
- break;
- }
-
- String provider = args[++i];
- if ("mina".equals(provider)) {
- System.setProperty(IoServiceFactory.class.getName(), BuiltinIoServiceFactoryFactories.MINA.getFactoryClassName());
- } else if ("nio2".endsWith(provider)) {
- System.setProperty(IoServiceFactory.class.getName(), BuiltinIoServiceFactoryFactories.NIO2.getFactoryClassName());
- } else {
- System.err.println("provider should be mina or nio2: " + argName);
- error = true;
- break;
- }
- } else if ("-o".equals(argName)) {
- if ((i + 1) >= numArgs) {
- System.err.println("option requires and argument: " + argName);
- error = true;
- break;
- }
- String opt = args[++i];
- int idx = opt.indexOf('=');
- if (idx <= 0) {
- System.err.println("bad syntax for option: " + opt);
- error = true;
- break;
- }
- options.put(opt.substring(0, idx), opt.substring(idx + 1));
- } else if (argName.startsWith("-")) {
- System.err.println("illegal option: " + argName);
- error = true;
- break;
- } else {
- System.err.println("extra argument: " + argName);
- error = true;
- break;
- }
- }
- if (error) {
- System.err.println("usage: sshfs [-p port] [-io mina|nio2] [-o option=value]");
- System.exit(-1);
- }
-
- SshServer sshd = Utils.setupTestServer(SshFsMounter.class);
- Map<String, Object> props = sshd.getProperties();
- props.putAll(options);
- PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options);
- File targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(MounterCommandFactory.class), "Failed to detect target folder");
- if (SecurityUtils.isBouncyCastleRegistered()) {
- sshd.setKeyPairProvider(SecurityUtils.createGeneratorHostKeyProvider(new File(targetFolder, "key.pem").toPath()));
- } else {
- sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File(targetFolder, "key.ser")));
- }
- // Should come AFTER key pair provider setup so auto-welcome can be generated if needed
- SshServer.setupServerBanner(sshd, resolver);
-
- sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE);
- sshd.setPasswordAuthenticator(AcceptAllPasswordAuthenticator.INSTANCE);
- sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
- sshd.setCommandFactory(new ScpCommandFactory.Builder().withDelegate(MounterCommandFactory.INSTANCE).build());
- sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
- sshd.setPort(port);
-
- System.err.println("Starting SSHD on port " + port);
- sshd.start();
- Thread.sleep(Long.MAX_VALUE);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java b/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java
new file mode 100644
index 0000000..bc34c25
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java
@@ -0,0 +1,155 @@
+/*
+ * 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.util.test;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.ChannelListener;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class TestChannelListener extends AbstractLoggingBean implements ChannelListener, NamedResource {
+ private final String name;
+ private final Collection<Channel> activeChannels = new CopyOnWriteArraySet<>();
+ private final Semaphore activeChannelsCounter = new Semaphore(0);
+ private final Collection<Channel> openChannels = new CopyOnWriteArraySet<>();
+ private final Semaphore openChannelsCounter = new Semaphore(0);
+ private final Collection<Channel> failedChannels = new CopyOnWriteArraySet<>();
+ private final Semaphore failedChannelsCounter = new Semaphore(0);
+ private final Map<Channel, Collection<String>> channelStateHints = new ConcurrentHashMap<>();
+ private final Semaphore chanelStateCounter = new Semaphore(0);
+ private final Semaphore modificationsCounter = new Semaphore(0);
+ private final Semaphore closedChannelsCounter = new Semaphore(0);
+
+ public TestChannelListener(String discriminator) {
+ super(discriminator);
+ name = discriminator;
+ }
+
+ public boolean waitForModification(long timeout, TimeUnit unit) throws InterruptedException {
+ return modificationsCounter.tryAcquire(timeout, unit);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public Collection<Channel> getActiveChannels() {
+ return activeChannels;
+ }
+
+ @Override
+ public void channelInitialized(Channel channel) {
+ Assert.assertTrue("Same channel instance re-initialized: " + channel, activeChannels.add(channel));
+ activeChannelsCounter.release();
+ modificationsCounter.release();
+ log.info("channelInitialized({})", channel);
+ }
+
+ public boolean waitForActiveChannelsChange(long timeout, TimeUnit unit) throws InterruptedException {
+ return activeChannelsCounter.tryAcquire(timeout, unit);
+ }
+
+ public Collection<Channel> getOpenChannels() {
+ return openChannels;
+ }
+
+ @Override
+ public void channelOpenSuccess(Channel channel) {
+ Assert.assertTrue("Open channel not activated: " + channel, activeChannels.contains(channel));
+ Assert.assertTrue("Same channel instance re-opened: " + channel, openChannels.add(channel));
+ openChannelsCounter.release();
+ modificationsCounter.release();
+ log.info("channelOpenSuccess({})", channel);
+ }
+
+ public boolean waitForOpenChannelsChange(long timeout, TimeUnit unit) throws InterruptedException {
+ return openChannelsCounter.tryAcquire(timeout, unit);
+ }
+
+ public Collection<Channel> getFailedChannels() {
+ return failedChannels;
+ }
+
+ @Override
+ public void channelOpenFailure(Channel channel, Throwable reason) {
+ Assert.assertTrue("Failed channel not activated: " + channel, activeChannels.contains(channel));
+ Assert.assertTrue("Same channel instance re-failed: " + channel, failedChannels.add(channel));
+ failedChannelsCounter.release();
+ modificationsCounter.release();
+ log.warn("channelOpenFailure({}) {} : {}", channel, reason.getClass().getSimpleName(), reason.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("channelOpenFailure(" + channel + ") details", reason);
+ }
+ }
+
+ public boolean waitForFailedChannelsChange(long timeout, TimeUnit unit) throws InterruptedException {
+ return failedChannelsCounter.tryAcquire(timeout, unit);
+ }
+
+ @Override
+ public void channelClosed(Channel channel, Throwable reason) {
+ Assert.assertTrue("Unknown closed channel instance: " + channel, activeChannels.remove(channel));
+ activeChannelsCounter.release();
+ closedChannelsCounter.release();
+ modificationsCounter.release();
+ log.info("channelClosed({})", channel);
+ }
+
+ public boolean waitForClosedChannelsChange(long timeout, TimeUnit unit) throws InterruptedException {
+ return closedChannelsCounter.tryAcquire(timeout, unit);
+ }
+
+ public Map<Channel, Collection<String>> getChannelStateHints() {
+ return channelStateHints;
+ }
+
+ @Override
+ public void channelStateChanged(Channel channel, String hint) {
+ Collection<String> hints;
+ synchronized (channelStateHints) {
+ hints = channelStateHints.get(channel);
+ if (hints == null) {
+ hints = new CopyOnWriteArrayList<>();
+ channelStateHints.put(channel, hints);
+ }
+ }
+
+ hints.add(hint);
+ chanelStateCounter.release();
+ modificationsCounter.release();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + getName() + "]";
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-git/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-git/pom.xml b/sshd-git/pom.xml
index d0f6c6a..f9e16a7 100644
--- a/sshd-git/pom.xml
+++ b/sshd-git/pom.xml
@@ -61,6 +61,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-mina/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index b14b18f..0234e85 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -50,6 +50,19 @@
<!-- test dependencies -->
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>net.i2p.crypto</groupId>
<artifactId>eddsa</artifactId>
<scope>test</scope>
@@ -107,20 +120,54 @@
</dependencies>
<build>
- <testSourceDirectory>${projectRoot}/sshd-core/src/test/java</testSourceDirectory>
- <resources>
- <resource>
- <directory>src/main/filtered-resources</directory>
- <filtering>true</filtering>
- </resource>
- <resource>
- <directory>${projectRoot}/sshd-core/src/test/resources</directory>
- <targetPath>${project.build.testOutputDirectory}</targetPath>
- </resource>
- </resources>
+ <testSourceDirectory>${build.directory}/test-sources</testSourceDirectory>
+ <testResources>
+ <testResource>
+ <directory>${build.directory}/test-resources</directory>
+ </testResource>
+ </testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-test-resources</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${build.directory}/test-resources</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${projectRoot}/sshd-core/src/test/resources</directory>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-test-sources</id>
+ <phase>generate-test-sources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${build.directory}/test-sources</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${projectRoot}/sshd-core/src/test/java</directory>
+ </resource>
+ <resource>
+ <directory>${projectRoot}/sshd-sftp/src/test/java</directory>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-sftp/pom.xml b/sshd-sftp/pom.xml
new file mode 100644
index 0000000..7d4f74a
--- /dev/null
+++ b/sshd-sftp/pom.xml
@@ -0,0 +1,103 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd</artifactId>
+ <version>1.7.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>sshd-sftp</artifactId>
+ <name>Apache Mina SSHD :: SFTP</name>
+ <packaging>jar</packaging>
+ <inceptionYear>2018</inceptionYear>
+
+ <properties>
+ <projectRoot>${project.basedir}/..</projectRoot>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jsch</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jzlib</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <additionalparam>-Xdoclint:none</additionalparam>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java
new file mode 100644
index 0000000..1034046
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.simple;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.Channel;
+import java.security.KeyPair;
+import java.util.Objects;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * A simplified <U>synchronous</U> API for obtaining SFTP sessions.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SimpleSftpClient extends Channel {
+ /**
+ * Creates an SFTP session on the default port and logs in using the provided credentials
+ *
+ * @param host The target host name or address
+ * @param username Username
+ * @param password Password
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(String host, String username, String password) throws IOException {
+ return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, username, password);
+ }
+
+ /**
+ * Creates an SFTP session using the provided credentials
+ *
+ * @param host The target host name or address
+ * @param port The target port
+ * @param username Username
+ * @param password Password
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(String host, int port, String username, String password) throws IOException {
+ return sftpLogin(InetAddress.getByName(ValidateUtils.checkNotNullAndNotEmpty(host, "No host")), port, username, password);
+ }
+
+ /**
+ * Creates an SFTP session on the default port and logs in using the provided credentials
+ *
+ * @param host The target host name or address
+ * @param username Username
+ * @param identity The {@link KeyPair} identity
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(String host, String username, KeyPair identity) throws IOException {
+ return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, username, identity);
+ }
+
+ /**
+ * Creates an SFTP session using the provided credentials
+ *
+ * @param host The target host name or address
+ * @param port The target port
+ * @param username Username
+ * @param identity The {@link KeyPair} identity
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(String host, int port, String username, KeyPair identity) throws IOException {
+ return sftpLogin(InetAddress.getByName(ValidateUtils.checkNotNullAndNotEmpty(host, "No host")), port, username, identity);
+ }
+
+ /**
+ * Creates an SFTP session on the default port and logs in using the provided credentials
+ *
+ * @param host The target host {@link InetAddress}
+ * @param username Username
+ * @param password Password
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(InetAddress host, String username, String password) throws IOException {
+ return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, username, password);
+ }
+
+ /**
+ * Creates an SFTP session using the provided credentials
+ *
+ * @param host The target host {@link InetAddress}
+ * @param port The target port
+ * @param username Username
+ * @param password Password
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(InetAddress host, int port, String username, String password) throws IOException {
+ return sftpLogin(new InetSocketAddress(Objects.requireNonNull(host, "No host address"), port), username, password);
+ }
+
+ /**
+ * Creates an SFTP session on the default port and logs in using the provided credentials
+ *
+ * @param host The target host {@link InetAddress}
+ * @param username Username
+ * @param identity The {@link KeyPair} identity
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(InetAddress host, String username, KeyPair identity) throws IOException {
+ return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, username, identity);
+ }
+
+ /**
+ * Creates an SFTP session using the provided credentials
+ *
+ * @param host The target host {@link InetAddress}
+ * @param port The target port
+ * @param username Username
+ * @param identity The {@link KeyPair} identity
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ default SftpClient sftpLogin(InetAddress host, int port, String username, KeyPair identity) throws IOException {
+ return sftpLogin(new InetSocketAddress(Objects.requireNonNull(host, "No host address"), port), username, identity);
+ }
+
+ /**
+ * Creates an SFTP session using the provided credentials
+ *
+ * @param target The target {@link SocketAddress}
+ * @param username Username
+ * @param password Password
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ SftpClient sftpLogin(SocketAddress target, String username, String password) throws IOException;
+
+ /**
+ * Creates an SFTP session using the provided credentials
+ *
+ * @param target The target {@link SocketAddress}
+ * @param username Username
+ * @param identity The {@link KeyPair} identity
+ * @return Created {@link SftpClient} - <B>Note:</B> closing the client also closes its
+ * underlying session
+ * @throws IOException If failed to login or authenticate
+ */
+ SftpClient sftpLogin(SocketAddress target, String username, KeyPair identity) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
new file mode 100644
index 0000000..09a7007
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.simple;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+public class SimpleSftpClientImpl extends AbstractLoggingBean implements SimpleSftpClient {
+
+ private SimpleClient client;
+ private SftpClientFactory sftpClientFactory;
+
+ public SimpleSftpClientImpl(SimpleClient client) {
+ this(client, null);
+ }
+
+ public SimpleSftpClientImpl(SimpleClient client, SftpClientFactory sftpClientFactory) {
+ this.client = client;
+ this.sftpClientFactory = sftpClientFactory != null ? sftpClientFactory : SftpClientFactory.instance();
+ }
+
+ public SimpleClient getClient() {
+ return client;
+ }
+
+ public void setClient(SimpleClient client) {
+ this.client = client;
+ }
+
+ public SftpClientFactory getSftpClientFactory() {
+ return sftpClientFactory;
+ }
+
+ public void setSftpClientFactory(SftpClientFactory sftpClientFactory) {
+ this.sftpClientFactory = sftpClientFactory;
+ }
+
+ @Override
+ public SftpClient sftpLogin(SocketAddress target, String username, String password) throws IOException {
+ return createSftpClient(client.sessionLogin(target, username, password));
+ }
+
+ @Override
+ public SftpClient sftpLogin(SocketAddress target, String username, KeyPair identity) throws IOException {
+ return createSftpClient(client.sessionLogin(target, username, identity));
+ }
+
+ protected SftpClient createSftpClient(final ClientSession session) throws IOException {
+ Exception err = null;
+ try {
+ SftpClient client = sftpClientFactory.createSftpClient(session);
+ try {
+ return createSftpClient(session, client);
+ } catch (Exception e) {
+ err = GenericUtils.accumulateException(err, e);
+ try {
+ client.close();
+ } catch (Exception t) {
+ if (log.isDebugEnabled()) {
+ log.debug("createSftpClient({}) failed ({}) to close client: {}",
+ session, t.getClass().getSimpleName(), t.getMessage());
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("createSftpClient(" + session + ") client close failure details", t);
+ }
+ err = GenericUtils.accumulateException(err, t);
+ }
+ }
+ } catch (Exception e) {
+ err = GenericUtils.accumulateException(err, e);
+ }
+
+ // This point is reached if error occurred
+ log.warn("createSftpClient({}) failed ({}) to create session: {}",
+ session, err.getClass().getSimpleName(), err.getMessage());
+
+ try {
+ session.close();
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ log.debug("createSftpClient({}) failed ({}) to close session: {}",
+ session, e.getClass().getSimpleName(), e.getMessage());
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("createSftpClient(" + session + ") session close failure details", e);
+ }
+ err = GenericUtils.accumulateException(err, e);
+ }
+
+ if (err instanceof IOException) {
+ throw (IOException) err;
+ } else {
+ throw new IOException(err);
+ }
+ }
+
+ protected SftpClient createSftpClient(final ClientSession session, final SftpClient client) throws IOException {
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?>[] interfaces = {SftpClient.class};
+ return (SftpClient) Proxy.newProxyInstance(loader, interfaces, (proxy, method, args) -> {
+ Throwable err = null;
+ Object result = null;
+ String name = method.getName();
+ try {
+ result = method.invoke(client, args);
+ } catch (Throwable t) {
+ if (log.isTraceEnabled()) {
+ log.trace("invoke(SftpClient#{}) failed ({}) to execute: {}",
+ name, t.getClass().getSimpleName(), t.getMessage());
+ }
+ err = GenericUtils.accumulateException(err, t);
+ }
+
+ // propagate the "close" call to the session as well
+ if ("close".equals(name) && GenericUtils.isEmpty(args)) {
+ try {
+ session.close();
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("invoke(ClientSession#{}) failed ({}) to execute: {}",
+ name, t.getClass().getSimpleName(), t.getMessage());
+ }
+ err = GenericUtils.accumulateException(err, t);
+ }
+ }
+
+ if (err != null) {
+ throw err;
+ }
+
+ return result;
+ });
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
new file mode 100644
index 0000000..676a03e
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.subsystem.sftp;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface RawSftpClient {
+ /**
+ * @param cmd Command to send - <B>Note:</B> only lower 8-bits are used
+ * @param buffer The {@link Buffer} containing the command data
+ * @return The assigned request id
+ * @throws IOException if failed to send command
+ */
+ int send(int cmd, Buffer buffer) throws IOException;
+
+ /**
+ * @param id The expected request id
+ * @return The received response {@link Buffer} containing the request id
+ * @throws IOException If connection closed or interrupted
+ */
+ Buffer receive(int id) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
new file mode 100644
index 0000000..7cada6e
--- /dev/null
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.List;
+
+import org.apache.sshd.client.subsystem.sftp.impl.AbstractSftpFileAttributeView;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpAclFileAttributeView extends AbstractSftpFileAttributeView implements AclFileAttributeView {
+ public SftpAclFileAttributeView(SftpFileSystemProvider provider, Path path, LinkOption... options) {
+ super(provider, path, options);
+ }
+
+ @Override
+ public UserPrincipal getOwner() throws IOException {
+ PosixFileAttributes v = provider.readAttributes(path, PosixFileAttributes.class, options);
+ return v.owner();
+ }
+
+ @Override
+ public void setOwner(UserPrincipal owner) throws IOException {
+ provider.setAttribute(path, "posix", "owner", owner, options);
+ }
+
+ @Override
+ public String name() {
+ return "acl";
+ }
+
+ @Override
+ public List<AclEntry> getAcl() throws IOException {
+ return readRemoteAttributes().getAcl();
+ }
+
+ @Override
+ public void setAcl(List<AclEntry> acl) throws IOException {
+ writeRemoteAttributes(new SftpClient.Attributes().acl(acl));
+ }
+
+}