Skip to content

Commit

Permalink
SCP remote path escaping is now configurable (Fixes #212, #184, #152)
Browse files Browse the repository at this point in the history
  • Loading branch information
hierynomus committed Sep 21, 2015
1 parent 28a11b0 commit d520585
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 150 deletions.
28 changes: 14 additions & 14 deletions src/main/java/net/schmizz/sshj/xfer/scp/SCPDownloadClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.scp.SCPEngine.Arg;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static net.schmizz.sshj.xfer.scp.SCPEngine.SCPArgument;
import static net.schmizz.sshj.xfer.scp.SCPEngine.SCPArguments;

/** Support for uploading files over a connected link using SCP. */
public final class SCPDownloadClient extends AbstractSCPClient {

Expand All @@ -43,11 +39,15 @@ public final class SCPDownloadClient extends AbstractSCPClient {
}

/** Download a file from {@code sourcePath} on the connected host to {@code targetPath} locally. */
public synchronized int copy(String sourcePath, LocalDestFile targetFile)
public synchronized int copy(String sourcePath, LocalDestFile targetFile) throws IOException {
return copy(sourcePath, targetFile, ScpCommandLine.EscapeMode.NoEscape);
}

public synchronized int copy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourcePath, targetFile);
startCopy(sourcePath, targetFile, escapeMode);
} finally {
engine.exit();
}
Expand All @@ -62,15 +62,15 @@ public void setRecursiveMode(boolean recursive) {
this.recursiveMode = recursive;
}

void startCopy(String sourcePath, LocalDestFile targetFile)
private void startCopy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
List<SCPArgument> args = SCPArguments.with(Arg.SOURCE)
.and(Arg.QUIET)
.and(Arg.PRESERVE_TIMES)
.and(Arg.RECURSIVE, recursiveMode)
.and(Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0))
.arguments();
engine.execSCPWith(args, sourcePath);
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SOURCE)
.and(ScpCommandLine.Arg.QUIET)
.and(ScpCommandLine.Arg.PRESERVE_TIMES)
.and(ScpCommandLine.Arg.RECURSIVE, recursiveMode)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
commandLine.withPath(sourcePath, escapeMode);
engine.execSCPWith(commandLine);

engine.signal("Start status OK");

Expand Down
117 changes: 3 additions & 114 deletions src/main/java/net/schmizz/sshj/xfer/scp/SCPEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

/** @see <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
class SCPEngine {

enum Arg {
SOURCE('f'),
SINK('t'),
RECURSIVE('r'),
VERBOSE('v'),
PRESERVE_TIMES('p'),
QUIET('q'),
LIMIT('l');

private final char a;

private Arg(char a) {
this.a = a;
}

@Override
public String toString() {
return "-" + a;
}
}

private static final String SCP_COMMAND = "scp";
private static final char LF = '\n';

private final Logger log = LoggerFactory.getLogger(getClass());
Expand Down Expand Up @@ -99,19 +79,9 @@ void cleanSlate() {
exitStatus = -1;
}

void execSCPWith(List<SCPArgument> args, String path)
void execSCPWith(ScpCommandLine commandLine)
throws SSHException {
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
for (SCPArgument arg : args) {
cmd.append(" ").append(arg);
}
cmd.append(" ");
if (path == null || path.isEmpty()) {
cmd.append(".");
} else {
cmd.append("'").append(path.replaceAll("'", "\\'")).append("'");
}
scp = host.startSession().exec(cmd.toString());
scp = host.startSession().exec(commandLine.toCommandLine());
}

void exit() {
Expand Down Expand Up @@ -187,85 +157,4 @@ long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long
TransferListener getTransferListener() {
return listener;
}

public static class SCPArgument {

private Arg name;
private String value;

private SCPArgument(Arg name, String value) {
this.name = name;
this.value = value;
}

public static SCPArgument addArgument(Arg name, String value) {
return new SCPArgument(name, value);
}

@Override
public String toString() {
String option = name.toString();
if (value != null) {
option = option + value;
}
return option;
}
}

public static class SCPArguments {

private static List<SCPArgument> args = null;

private SCPArguments() {
this.args = new LinkedList<SCPArgument>();
}

private static void addArgument(Arg name, String value, boolean accept) {
if (accept) {
args.add(SCPArgument.addArgument(name, value));
}
}

public static SCPArguments with(Arg name) {
return with(name, null, true);
}

public static SCPArguments with(Arg name, String value) {
return with(name, value, true);
}

public static SCPArguments with(Arg name, boolean accept) {
return with(name, null, accept);
}

public static SCPArguments with(Arg name, String value, boolean accept) {
SCPArguments scpArguments = new SCPArguments();
addArgument(name, value, accept);
return scpArguments;
}

public SCPArguments and(Arg name) {
addArgument(name, null, true);
return this;
}

public SCPArguments and(Arg name, String value) {
addArgument(name, value, true);
return this;
}

public SCPArguments and(Arg name, boolean accept) {
addArgument(name, null, accept);
return this;
}

public SCPArguments and(Arg name, String value, boolean accept) {
addArgument(name, value, accept);
return this;
}

public List<SCPArgument> arguments() {
return args;
}
}
}
34 changes: 17 additions & 17 deletions src/main/java/net/schmizz/sshj/xfer/scp/SCPUploadClient.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright 2009 sshj contributors
*
* <p/>
* Licensed 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
*
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.
Expand All @@ -20,14 +20,9 @@
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.scp.SCPEngine.Arg;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import static net.schmizz.sshj.xfer.scp.SCPEngine.SCPArgument;
import static net.schmizz.sshj.xfer.scp.SCPEngine.SCPArguments;

/** Support for uploading files over a connected link using SCP. */
public final class SCPUploadClient extends AbstractSCPClient {
Expand All @@ -45,9 +40,14 @@ public final class SCPUploadClient extends AbstractSCPClient {
/** Upload a local file from {@code localFile} to {@code targetPath} on the remote host. */
public synchronized int copy(LocalSourceFile sourceFile, String remotePath)
throws IOException {
return copy(sourceFile, remotePath, ScpCommandLine.EscapeMode.SingleQuote);
}

public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourceFile, remotePath);
startCopy(sourceFile, remotePath, escapeMode);
} finally {
engine.exit();
}
Expand All @@ -58,14 +58,14 @@ public void setUploadFilter(LocalFileFilter uploadFilter) {
this.uploadFilter = uploadFilter;
}

private synchronized void startCopy(LocalSourceFile sourceFile, String targetPath)
private void startCopy(LocalSourceFile sourceFile, String targetPath, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
List<SCPArgument> args = SCPArguments.with(Arg.SINK)
.and(Arg.RECURSIVE)
.and(Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime())
.and(Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0))
.arguments();
engine.execSCPWith(args, targetPath);
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SINK)
.and(ScpCommandLine.Arg.RECURSIVE)
.and(ScpCommandLine.Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime())
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
commandLine.withPath(targetPath, escapeMode);
engine.execSCPWith(commandLine);
engine.check("Start status OK");
process(engine.getTransferListener(), sourceFile);
}
Expand Down
Loading

0 comments on commit d520585

Please sign in to comment.