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 2013/07/22 20:37:02 UTC
[5/9] git commit: Provide an SCP client
Provide an SCP client
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/16fc6a80
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/16fc6a80
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/16fc6a80
Branch: refs/heads/master
Commit: 16fc6a80714f2322479eaa5dbffea4e6f179a887
Parents: c91e7de
Author: Guillaume Nodet <gn...@apache.org>
Authored: Sun Jul 21 21:56:59 2013 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Sun Jul 21 21:56:59 2013 +0200
----------------------------------------------------------------------
.../java/org/apache/sshd/ClientChannel.java | 4 +
.../java/org/apache/sshd/ClientSession.java | 6 +
.../main/java/org/apache/sshd/SshServer.java | 3 +-
.../java/org/apache/sshd/client/ScpClient.java | 23 ++
.../client/channel/AbstractClientChannel.java | 10 +
.../sshd/client/channel/ChannelSession.java | 15 +-
.../sshd/client/scp/DefaultScpClient.java | 151 ++++++++
.../sshd/client/session/ClientSessionImpl.java | 6 +
.../org/apache/sshd/common/FactoryManager.java | 4 +-
.../org/apache/sshd/common/scp/ScpHelper.java | 363 +++++++++++++++++++
.../apache/sshd/server/command/ScpCommand.java | 331 ++---------------
11 files changed, 601 insertions(+), 315 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java b/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java
index d19d12f..4b01b0a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/ClientChannel.java
@@ -56,6 +56,10 @@ public interface ClientChannel {
*/
OutputStream getInvertedIn();
+ InputStream getInvertedOut();
+
+ InputStream getInvertedErr();
+
/**
* Set an input stream that will be read by this channel and forwarded to
* the remote channel. Note that using such a stream will create an additional
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
index 8cd7f22..6b7b1af 100644
--- a/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
@@ -23,6 +23,7 @@ import java.security.KeyPair;
import java.util.Map;
import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.ScpClient;
import org.apache.sshd.client.channel.ChannelDirectTcpip;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ChannelShell;
@@ -112,6 +113,11 @@ public interface ClientSession extends Session {
ChannelDirectTcpip createDirectTcpipChannel(SshdSocketAddress local, SshdSocketAddress remote) throws IOException;
/**
+ * Create an SCP client from this session.
+ */
+ ScpClient createScpClient();
+
+ /**
* Start forwarding the given local address on the client to the given address on the server.
*/
SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
index 4edab9b..acc1ca1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -60,6 +60,7 @@ import org.apache.sshd.common.cipher.ARCFOUR256;
import org.apache.sshd.common.cipher.BlowfishCBC;
import org.apache.sshd.common.cipher.TripleDESCBC;
import org.apache.sshd.common.compression.CompressionNone;
+import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
import org.apache.sshd.common.forward.DefaultForwardingAcceptorFactory;
import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory;
import org.apache.sshd.common.forward.TcpipServerChannel;
@@ -79,7 +80,6 @@ import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.common.file.FileSystemFactory;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.ServerFactoryManager;
@@ -90,7 +90,6 @@ import org.apache.sshd.server.auth.UserAuthPublicKey;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.auth.gss.UserAuthGSS;
import org.apache.sshd.server.channel.ChannelSession;
-import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/client/ScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/ScpClient.java
new file mode 100644
index 0000000..fc6bfb8
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ScpClient.java
@@ -0,0 +1,23 @@
+package org.apache.sshd.client;
+
+import java.io.IOException;
+
+/**
+ */
+public interface ScpClient {
+
+ void download(String remote, String local) throws IOException;
+
+ void download(String remote, String local, boolean recursive) throws IOException;
+
+ void download(String[] remote, String local) throws Exception;
+
+ void download(String[] remote, String local, boolean recursive) throws Exception;
+
+ void upload(String remote, String local) throws IOException;
+
+ void upload(String remote, String local, boolean recursive) throws IOException;
+
+ void upload(String[] local, String remote, boolean recursive) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
index 715ae82..40015c3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
@@ -45,7 +45,9 @@ public abstract class AbstractClientChannel extends AbstractChannel implements C
protected InputStream in;
protected OutputStream invertedIn;
protected OutputStream out;
+ protected InputStream invertedOut;
protected OutputStream err;
+ protected InputStream invertedErr;
protected Integer exitStatus;
protected String exitSignal;
protected int openFailureReason;
@@ -68,6 +70,10 @@ public abstract class AbstractClientChannel extends AbstractChannel implements C
this.in = in;
}
+ public InputStream getInvertedOut() {
+ return invertedOut;
+ }
+
public OutputStream getOut() {
return out;
}
@@ -76,6 +82,10 @@ public abstract class AbstractClientChannel extends AbstractChannel implements C
this.out = out;
}
+ public InputStream getInvertedErr() {
+ return invertedErr;
+ }
+
public OutputStream getErr() {
return err;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelSession.java
index fe2ef23..ee48e0d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/ChannelSession.java
@@ -24,6 +24,8 @@ import java.io.InputStream;
import org.apache.sshd.client.future.OpenFuture;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.channel.ChannelPipedInputStream;
+import org.apache.sshd.common.channel.ChannelPipedOutputStream;
import org.apache.sshd.common.util.Buffer;
/**
@@ -41,8 +43,17 @@ public class ChannelSession extends AbstractClientChannel {
public OpenFuture open() throws IOException {
invertedIn = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
- if (out == null || err == null) {
- throw new IllegalStateException("in, out and err streams should be set before opening channel");
+ if (out == null) {
+ ChannelPipedInputStream pis = new ChannelPipedInputStream(localWindow);
+ ChannelPipedOutputStream pos = new ChannelPipedOutputStream(pis);
+ out = pos;
+ invertedOut = pis;
+ }
+ if (err == null) {
+ ChannelPipedInputStream pis = new ChannelPipedInputStream(localWindow);
+ ChannelPipedOutputStream pos = new ChannelPipedOutputStream(pis);
+ err = pos;
+ invertedErr = pis;
}
return internalOpen();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
new file mode 100644
index 0000000..2bd6985
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
@@ -0,0 +1,151 @@
+package org.apache.sshd.client.scp;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.Arrays;
+
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.client.ScpClient;
+import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.file.FileSystemFactory;
+import org.apache.sshd.common.file.FileSystemView;
+import org.apache.sshd.common.file.SshFile;
+import org.apache.sshd.common.scp.ScpHelper;
+
+/**
+ */
+public class DefaultScpClient implements ScpClient {
+
+ private final ClientSession clientSession;
+
+ public DefaultScpClient(ClientSession clientSession) {
+ this.clientSession = clientSession;
+ }
+
+ public void download(String remote, String local) throws IOException {
+ download(new String[] { remote }, local, false, false);
+ }
+
+ public void download(String remote, String local, boolean recursive) throws IOException {
+ download(new String[] { remote }, local, recursive, false);
+ }
+
+ public void download(String[] remote, String local) throws IOException {
+ download(remote, local, false, true);
+ }
+
+ public void download(String[] remote, String local, boolean recursive) throws IOException {
+ download(remote, local, recursive, true);
+ }
+
+ private void download(String[] remote, String local, boolean recursive, boolean shouldBeDir) throws IOException {
+ local = checkNotNullAndNotEmpty(local, "Invalid argument local: {}");
+ remote = checkNotNullAndNotEmpty(remote, "Invalid argument remote: {}");
+ StringBuilder sb = new StringBuilder("scp");
+ if (recursive) {
+ sb.append(" -r");
+ }
+ sb.append(" -f");
+ for (String r : remote) {
+ r = checkNotNullAndNotEmpty(r, "Invalid argument remote: {}");
+ sb.append(" ").append(r);
+ }
+
+ FileSystemFactory factory = clientSession.getFactoryManager().getFileSystemFactory();
+ FileSystemView fs = factory.createFileSystemView(clientSession);
+ SshFile target = fs.getFile(local);
+ if (shouldBeDir) {
+ if (!target.doesExist()) {
+ throw new SshException("Target directory " + target.toString() + " does not exists");
+ }
+ if (!target.isDirectory()) {
+ throw new SshException("Target directory " + target.toString() + " is not a directory");
+ }
+ }
+
+ ChannelExec channel = clientSession.createExecChannel(sb.toString());
+ try {
+ channel.open().await();
+ } catch (InterruptedException e) {
+ throw (IOException) new InterruptedIOException().initCause(e);
+ }
+
+ ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs);
+
+ helper.receive(target, recursive, shouldBeDir);
+
+ channel.close(false);
+ }
+
+ public void upload(String remote, String local) throws IOException {
+ upload(new String[] { remote }, local, false, false);
+ }
+
+ public void upload(String remote, String local, boolean recursive) throws IOException {
+ upload(new String[] { remote }, local, recursive, false);
+ }
+
+ public void upload(String[] local, String remote) throws IOException {
+ upload(local, remote, false, true);
+ }
+
+ public void upload(String[] local, String remote, boolean recursive) throws IOException {
+ upload(local, remote, false, true);
+ }
+
+ private void upload(String[] local, String remote, boolean recursive, boolean shouldBeDir) throws IOException {
+ local = checkNotNullAndNotEmpty(local, "Invalid argument local: {}");
+ remote = checkNotNullAndNotEmpty(remote, "Invalid argument remote: {}");
+ StringBuilder sb = new StringBuilder("scp");
+ if (recursive) {
+ sb.append(" -r");
+ }
+ if (shouldBeDir) {
+ sb.append(" -d");
+ }
+ sb.append(" -t");
+ for (String r : local) {
+ r = checkNotNullAndNotEmpty(r, "Invalid argument remote: {}");
+ sb.append(" ").append(r);
+ }
+ ChannelExec channel = clientSession.createExecChannel(sb.toString());
+ try {
+ channel.open().await();
+ } catch (InterruptedException e) {
+ throw (IOException) new InterruptedIOException().initCause(e);
+ }
+
+ FileSystemFactory factory = clientSession.getFactoryManager().getFileSystemFactory();
+ FileSystemView fs = factory.createFileSystemView(clientSession);
+ ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs);
+ SshFile target = fs.getFile(remote);
+
+ helper.send(Arrays.asList(local), recursive);
+
+ channel.close(false);
+ }
+
+ private <T> T checkNotNull(T t, String message) {
+ if (t == null) {
+ throw new IllegalStateException(String.format(message, t));
+ }
+ return t;
+ }
+
+ private String checkNotNullAndNotEmpty(String t, String message) {
+ t = checkNotNull(t, message).trim();
+ if (t.isEmpty()) {
+ throw new IllegalArgumentException(String.format(message, t));
+ }
+ return t;
+ }
+
+ private <T> T[] checkNotNullAndNotEmpty(T[] t, String message) {
+ t = checkNotNull(t, message);
+ if (t.length == 0) {
+ throw new IllegalArgumentException(String.format(message, t));
+ }
+ return t;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index ccc21bc..10825b7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -29,6 +29,7 @@ import org.apache.mina.core.session.IoSession;
import org.apache.sshd.ClientChannel;
import org.apache.sshd.ClientSession;
import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.ScpClient;
import org.apache.sshd.client.ServerKeyVerifier;
import org.apache.sshd.client.UserAuth;
import org.apache.sshd.client.UserInteraction;
@@ -43,6 +44,7 @@ import org.apache.sshd.client.channel.ChannelSubsystem;
import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.future.DefaultAuthFuture;
import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.client.scp.DefaultScpClient;
import org.apache.sshd.common.Channel;
import org.apache.sshd.common.KeyExchange;
import org.apache.sshd.common.KeyPairProvider;
@@ -250,6 +252,10 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
return channel;
}
+ public ScpClient createScpClient() {
+ return new DefaultScpClient(this);
+ }
+
public SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
return getTcpipForwarder().startLocalPortForwarding(local, remote);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index 84886a6..28f5f4a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -175,8 +175,8 @@ public interface FactoryManager {
/**
* Retrieve the <code>FileSystemFactory</code> to be used to traverse the file system.
*
- * @return a valid <code>FileSystemFactory</code> object or <code>null</code> if commands
- * are not supported on this server
+ * @return a valid <code>FileSystemFactory</code> object or <code>null</code> if file based
+ * interactions are not supported on this server
*/
FileSystemFactory getFileSystemFactory();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
new file mode 100644
index 0000000..17b55c1
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
@@ -0,0 +1,363 @@
+/*
+ * 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.scp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.file.FileSystemView;
+import org.apache.sshd.common.file.SshFile;
+import org.apache.sshd.common.util.DirectoryScanner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ScpHelper {
+
+ protected static final Logger log = LoggerFactory.getLogger(ScpHelper.class);
+
+ public static final int OK = 0;
+ public static final int WARNING = 1;
+ public static final int ERROR = 2;
+
+ protected final FileSystemView root;
+ protected final InputStream in;
+ protected final OutputStream out;
+
+ public ScpHelper(InputStream in, OutputStream out, FileSystemView root) {
+ this.in = in;
+ this.out = out;
+ this.root = root;
+ }
+
+ public void receive(SshFile path, boolean recursive, boolean shouldBeDir) throws IOException {
+ if (shouldBeDir) {
+ if (!path.doesExist()) {
+ throw new SshException("Target directory " + path.toString() + " does not exists");
+ }
+ if (!path.isDirectory()) {
+ throw new SshException("Target directory " + path.toString() + " is not a directory");
+ }
+ }
+ ack();
+ for (;;)
+ {
+ String line;
+ boolean isDir = false;
+ int c = readAck(true);
+ switch (c)
+ {
+ case -1:
+ return;
+ case 'D':
+ isDir = true;
+ case 'C':
+ line = ((char) c) + readLine();
+ break;
+ case 'T':
+ readLine();
+ ack();
+ continue;
+ case 'E':
+ readLine();
+ return;
+ default:
+ //a real ack that has been acted upon already
+ continue;
+ }
+
+ if (recursive && isDir)
+ {
+ receiveDir(line, path);
+ }
+ else
+ {
+ receiveFile(line, path);
+ }
+ }
+ }
+
+
+ public void receiveDir(String header, SshFile path) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("Writing dir {}", path);
+ }
+ if (!header.startsWith("D")) {
+ throw new IOException("Expected a D message but got '" + header + "'");
+ }
+
+ String perms = header.substring(1, 5);
+ int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6)));
+ String name = header.substring(header.indexOf(' ', 6) + 1);
+
+ if (length != 0) {
+ throw new IOException("Expected 0 length for directory but got " + length);
+ }
+ SshFile file;
+ if (path.doesExist() && path.isDirectory()) {
+ file = root.getFile(path, name);
+ } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
+ file = path;
+ } else {
+ throw new IOException("Can not write to " + path);
+ }
+ if (!(file.doesExist() && file.isDirectory()) && !file.mkdir()) {
+ throw new IOException("Could not create directory " + file);
+ }
+
+ ack();
+
+ for (;;) {
+ header = readLine();
+ if (header.startsWith("C")) {
+ receiveFile(header, file);
+ } else if (header.startsWith("D")) {
+ receiveDir(header, file);
+ } else if (header.equals("E")) {
+ ack();
+ break;
+ } else if (header.equals("T")) {
+ ack();
+ break;
+ } else {
+ throw new IOException("Unexpected message: '" + header + "'");
+ }
+ }
+
+ }
+
+ public void receiveFile(String header, SshFile path) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("Writing file {}", path);
+ }
+ if (!header.startsWith("C")) {
+ throw new IOException("Expected a C message but got '" + header + "'");
+ }
+
+ String perms = header.substring(1, 5);
+ long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6)));
+ String name = header.substring(header.indexOf(' ', 6) + 1);
+
+ SshFile file;
+ if (path.doesExist() && path.isDirectory()) {
+ file = root.getFile(path, name);
+ } else if (path.doesExist() && path.isFile()) {
+ file = path;
+ } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
+ file = path;
+ } else {
+ throw new IOException("Can not write to " + path);
+ }
+ if (file.doesExist() && file.isDirectory()) {
+ throw new IOException("File is a directory: " + file);
+ } else if (file.doesExist() && !file.isWritable()) {
+ throw new IOException("Can not write to file: " + file);
+ }
+ OutputStream os = file.createOutputStream(0);
+ try {
+ ack();
+
+ byte[] buffer = new byte[8192];
+ while (length > 0) {
+ int len = (int) Math.min(length, buffer.length);
+ len = in.read(buffer, 0, len);
+ if (len <= 0) {
+ throw new IOException("End of stream reached");
+ }
+ os.write(buffer, 0, len);
+ length -= len;
+ }
+ } finally {
+ os.close();
+ }
+
+ ack();
+ readAck(false);
+ }
+
+ public String readLine() throws IOException {
+ return readLine(false);
+ }
+
+ public String readLine(boolean canEof) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (;;) {
+ int c = in.read();
+ if (c == '\n') {
+ return baos.toString();
+ } else if (c == -1) {
+ if (!canEof) {
+ throw new EOFException();
+ }
+ return null;
+ } else {
+ baos.write(c);
+ }
+ }
+ }
+
+ public void send(List<String> paths, boolean recursive) throws IOException {
+ for (String pattern : paths) {
+ int idx = pattern.indexOf('*');
+ if (idx >= 0) {
+ String basedir = "";
+ int lastSep = pattern.substring(0, idx).lastIndexOf('/');
+ if (lastSep >= 0) {
+ basedir = pattern.substring(0, lastSep);
+ pattern = pattern.substring(lastSep + 1);
+ }
+ String[] included = new DirectoryScanner(basedir, pattern).scan();
+ for (String path : included) {
+ SshFile file = root.getFile(basedir + "/" + path);
+ if (file.isFile()) {
+ sendFile(file);
+ } else if (file.isDirectory()) {
+ if (!recursive) {
+ out.write(ScpHelper.WARNING);
+ out.write((path + " not a regular file\n").getBytes());
+ } else {
+ sendDir(file);
+ }
+ } else {
+ out.write(ScpHelper.WARNING);
+ out.write((path + " unknown file type\n").getBytes());
+ }
+ }
+ } else {
+ String basedir = "";
+ int lastSep = pattern.lastIndexOf('/');
+ if (lastSep >= 0) {
+ basedir = pattern.substring(0, lastSep);
+ pattern = pattern.substring(lastSep + 1);
+ }
+ SshFile file = root.getFile(basedir + "/" + pattern);
+ if (!file.doesExist()) {
+ throw new IOException(file + ": no such file or directory");
+ }
+ if (file.isFile()) {
+ sendFile(file);
+ } else if (file.isDirectory()) {
+ if (!recursive) {
+ throw new IOException(file + " not a regular file");
+ } else {
+ sendDir(file);
+ }
+ } else {
+ throw new IOException(file + ": unknown file type");
+ }
+ }
+ }
+ }
+
+ public void sendFile(SshFile path) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("Reading file {}", path);
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append("C");
+ buf.append("0644"); // TODO: what about perms
+ buf.append(" ");
+ buf.append(path.getSize()); // length
+ buf.append(" ");
+ buf.append(path.getName());
+ buf.append("\n");
+ out.write(buf.toString().getBytes());
+ out.flush();
+ readAck(false);
+
+ InputStream is = path.createInputStream(0);
+ try {
+ byte[] buffer = new byte[8192];
+ for (;;) {
+ int len = is.read(buffer, 0, buffer.length);
+ if (len == -1) {
+ break;
+ }
+ out.write(buffer, 0, len);
+ }
+ } finally {
+ is.close();
+ }
+ ack();
+ readAck(false);
+ }
+
+ public void sendDir(SshFile path) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("Reading directory {}", path);
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append("D");
+ buf.append("0755"); // what about perms
+ buf.append(" ");
+ buf.append("0"); // length
+ buf.append(" ");
+ buf.append(path.getName());
+ buf.append("\n");
+ out.write(buf.toString().getBytes());
+ out.flush();
+ readAck(false);
+
+ for (SshFile child : path.listSshFiles()) {
+ if (child.isFile()) {
+ sendFile(child);
+ } else if (child.isDirectory()) {
+ sendDir(child);
+ }
+ }
+
+ out.write("E\n".getBytes());
+ out.flush();
+ readAck(false);
+ }
+
+ public void ack() throws IOException {
+ out.write(0);
+ out.flush();
+ }
+
+ public int readAck(boolean canEof) throws IOException {
+ int c = in.read();
+ switch (c) {
+ case -1:
+ if (!canEof) {
+ throw new EOFException();
+ }
+ break;
+ case OK:
+ break;
+ case WARNING:
+ log.warn("Received warning: " + readLine());
+ break;
+ case ERROR:
+ throw new IOException("Received nack: " + readLine());
+ default:
+ break;
+ }
+ return c;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/16fc6a80/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
index 950d569..8ecd7cd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
@@ -18,20 +18,19 @@
*/
package org.apache.sshd.server.command;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
-import org.apache.sshd.common.util.DirectoryScanner;
+import org.apache.sshd.common.file.FileSystemAware;
+import org.apache.sshd.common.file.FileSystemView;
+import org.apache.sshd.common.scp.ScpHelper;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
-import org.apache.sshd.common.file.FileSystemAware;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,19 +44,15 @@ import org.slf4j.LoggerFactory;
public class ScpCommand implements Command, Runnable, FileSystemAware {
protected static final Logger log = LoggerFactory.getLogger(ScpCommand.class);
- protected static final int OK = 0;
- protected static final int WARNING = 1;
- protected static final int ERROR = 2;
protected String name;
protected boolean optR;
protected boolean optT;
protected boolean optF;
- protected boolean optV;
protected boolean optD;
- protected boolean optP;
+ protected boolean optP; // TODO: handle modification times
protected FileSystemView root;
- protected String path;
+ protected List<String> paths;
protected InputStream in;
protected OutputStream out;
protected OutputStream err;
@@ -69,7 +64,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
if (log.isDebugEnabled()) {
log.debug("Executing command {}", name);
}
- path = ".";
+ paths = new ArrayList<String>();
for (int i = 1; i < args.length; i++) {
if (args[i].charAt(0) == '-') {
for (int j = 1; j < args[i].length(); j++) {
@@ -86,9 +81,6 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
case 't':
optT = true;
break;
- case 'v':
- optV = true;
- break;
case 'd':
optD = true;
break;
@@ -98,12 +90,15 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
}
} else if (i == args.length - 1) {
- path = args[args.length - 1];
+ paths.add(args[args.length - 1]);
}
}
if (!optF && !optT) {
error = new IOException("Either -f or -t option should be set");
}
+ if (optT && paths.size() != 1) {
+ error = new IOException("One and only one path must be given with -t option");
+ }
}
public void setInputStream(InputStream in) {
@@ -122,6 +117,10 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
this.callback = callback;
}
+ public void setFileSystemView(FileSystemView view) {
+ this.root = view;
+ }
+
public void start(Environment env) throws IOException {
if (error != null) {
throw error;
@@ -133,104 +132,20 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
public void run() {
- int exitValue = OK;
+ int exitValue = ScpHelper.OK;
String exitMessage = null;
-
+ ScpHelper helper = new ScpHelper(in, out, root);
try {
- if (optT)
- {
- ack();
- for (; ;)
- {
- String line;
- boolean isDir = false;
- int c = readAck(true);
- switch (c)
- {
- case -1:
- return;
- case 'D':
- isDir = true;
- case 'C':
- line = ((char) c) + readLine();
- break;
- case 'T':
- readLine();
- ack();
- continue;
- case 'E':
- readLine();
- return;
- default:
- //a real ack that has been acted upon already
- continue;
- }
-
- if (optR && isDir)
- {
- writeDir(line, root.getFile(path));
- }
- else
- {
- writeFile(line, root.getFile(path));
- }
- }
+ if (optT) {
+ helper.receive(root.getFile(paths.get(0)), optR, optD);
} else if (optF) {
- String pattern = path;
- int idx = pattern.indexOf('*');
- if (idx >= 0) {
- String basedir = "";
- int lastSep = pattern.substring(0, idx).lastIndexOf('/');
- if (lastSep >= 0) {
- basedir = pattern.substring(0, lastSep);
- pattern = pattern.substring(lastSep + 1);
- }
- String[] included = new DirectoryScanner(basedir, pattern).scan();
- for (String path : included) {
- SshFile file = root.getFile(basedir + "/" + path);
- if (file.isFile()) {
- readFile(file);
- } else if (file.isDirectory()) {
- if (!optR) {
- out.write(WARNING);
- out.write((path + " not a regular file\n").getBytes());
- } else {
- readDir(file);
- }
- } else {
- out.write(WARNING);
- out.write((path + " unknown file type\n").getBytes());
- }
- }
- } else {
- String basedir = "";
- int lastSep = pattern.lastIndexOf('/');
- if (lastSep >= 0) {
- basedir = pattern.substring(0, lastSep);
- pattern = pattern.substring(lastSep + 1);
- }
- SshFile file = root.getFile(basedir + "/" + pattern);
- if (!file.doesExist()) {
- throw new IOException(file + ": no such file or directory");
- }
- if (file.isFile()) {
- readFile(file);
- } else if (file.isDirectory()) {
- if (!optR) {
- throw new IOException(file + " not a regular file");
- } else {
- readDir(file);
- }
- } else {
- throw new IOException(file + ": unknown file type");
- }
- }
+ helper.send(paths, optR);
} else {
throw new IOException("Unsupported mode");
}
} catch (IOException e) {
try {
- exitValue = ERROR;
+ exitValue = ScpHelper.ERROR;
exitMessage = e.getMessage() == null ? "" : e.getMessage();
out.write(exitValue);
out.write(exitMessage.getBytes());
@@ -247,207 +162,5 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
}
- protected void writeDir(String header, SshFile path) throws IOException {
- if (log.isDebugEnabled()) {
- log.debug("Writing dir {}", path);
- }
- if (!header.startsWith("D")) {
- throw new IOException("Expected a D message but got '" + header + "'");
- }
-
- String perms = header.substring(1, 5);
- int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6)));
- String name = header.substring(header.indexOf(' ', 6) + 1);
-
- if (length != 0) {
- throw new IOException("Expected 0 length for directory but got " + length);
- }
- SshFile file;
- if (path.doesExist() && path.isDirectory()) {
- file = root.getFile(path, name);
- } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
- file = path;
- } else {
- throw new IOException("Can not write to " + path);
- }
- if (!(file.doesExist() && file.isDirectory()) && !file.mkdir()) {
- throw new IOException("Could not create directory " + file);
- }
-
- ack();
-
- for (;;) {
- header = readLine();
- if (header.startsWith("C")) {
- writeFile(header, file);
- } else if (header.startsWith("D")) {
- writeDir(header, file);
- } else if (header.equals("E")) {
- ack();
- break;
- } else if (header.equals("T")) {
- ack();
- break;
- } else {
- throw new IOException("Unexpected message: '" + header + "'");
- }
- }
-
- }
-
- protected void writeFile(String header, SshFile path) throws IOException {
- if (log.isDebugEnabled()) {
- log.debug("Writing file {}", path);
- }
- if (!header.startsWith("C")) {
- throw new IOException("Expected a C message but got '" + header + "'");
- }
-
- String perms = header.substring(1, 5);
- long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6)));
- String name = header.substring(header.indexOf(' ', 6) + 1);
-
- SshFile file;
- if (path.doesExist() && path.isDirectory()) {
- file = root.getFile(path, name);
- } else if (path.doesExist() && path.isFile()) {
- file = path;
- } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
- file = path;
- } else {
- throw new IOException("Can not write to " + path);
- }
- if (file.doesExist() && file.isDirectory()) {
- throw new IOException("File is a directory: " + file);
- } else if (file.doesExist() && !file.isWritable()) {
- throw new IOException("Can not write to file: " + file);
- }
- OutputStream os = file.createOutputStream(0);
- try {
- ack();
-
- byte[] buffer = new byte[8192];
- while (length > 0) {
- int len = (int) Math.min(length, buffer.length);
- len = in.read(buffer, 0, len);
- if (len <= 0) {
- throw new IOException("End of stream reached");
- }
- os.write(buffer, 0, len);
- length -= len;
- }
- } finally {
- os.close();
- }
-
- ack();
- readAck(false);
- }
-
- protected String readLine() throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- for (;;) {
- int c = in.read();
- if (c == '\n') {
- return baos.toString();
- } else if (c == -1) {
- throw new IOException("End of stream");
- } else {
- baos.write(c);
- }
- }
- }
-
- protected void readFile(SshFile path) throws IOException {
- if (log.isDebugEnabled()) {
- log.debug("Reading file {}", path);
- }
- StringBuffer buf = new StringBuffer();
- buf.append("C");
- buf.append("0644"); // what about perms
- buf.append(" ");
- buf.append(path.getSize()); // length
- buf.append(" ");
- buf.append(path.getName());
- buf.append("\n");
- out.write(buf.toString().getBytes());
- out.flush();
- readAck(false);
-
- InputStream is = path.createInputStream(0);
- try {
- byte[] buffer = new byte[8192];
- for (;;) {
- int len = is.read(buffer, 0, buffer.length);
- if (len == -1) {
- break;
- }
- out.write(buffer, 0, len);
- }
- } finally {
- is.close();
- }
- ack();
- readAck(false);
- }
-
- protected void readDir(SshFile path) throws IOException {
- if (log.isDebugEnabled()) {
- log.debug("Reading directory {}", path);
- }
- StringBuffer buf = new StringBuffer();
- buf.append("D");
- buf.append("0755"); // what about perms
- buf.append(" ");
- buf.append("0"); // length
- buf.append(" ");
- buf.append(path.getName());
- buf.append("\n");
- out.write(buf.toString().getBytes());
- out.flush();
- readAck(false);
-
- for (SshFile child : path.listSshFiles()) {
- if (child.isFile()) {
- readFile(child);
- } else if (child.isDirectory()) {
- readDir(child);
- }
- }
-
- out.write("E\n".getBytes());
- out.flush();
- readAck(false);
- }
-
- protected void ack() throws IOException {
- out.write(0);
- out.flush();
- }
-
- protected int readAck(boolean canEof) throws IOException {
- int c = in.read();
- switch (c) {
- case -1:
- if (!canEof) {
- throw new EOFException();
- }
- break;
- case OK:
- break;
- case WARNING:
- log.warn("Received warning: " + readLine());
- break;
- case ERROR:
- throw new IOException("Received nack: " + readLine());
- default:
- break;
- }
- return c;
- }
-
- public void setFileSystemView(FileSystemView view) {
- this.root = view;
- }
}