Skip to content

Commit

Permalink
Fixed #252 with backwards compatible behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
hierynomus committed Jul 4, 2016
1 parent 418f03f commit 5a64060
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 44 deletions.
127 changes: 90 additions & 37 deletions src/main/java/net/schmizz/sshj/sftp/SFTPFileTransfer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.EnumSet;
import java.util.List;

public class SFTPFileTransfer
extends AbstractFileTransfer
Expand Down Expand Up @@ -67,7 +68,7 @@ public void download(String source, String dest)
@Override
public void upload(LocalSourceFile localFile, String remotePath)
throws IOException {
new Uploader().upload(getTransferListener(), localFile, remotePath);
new Uploader(localFile, remotePath).upload(getTransferListener());
}

@Override
Expand Down Expand Up @@ -173,6 +174,35 @@ private void copyAttributes(final RemoteResourceInfo remote, final LocalDestFile

private class Uploader {

private final LocalSourceFile source;
private final String remote;

private Uploader(final LocalSourceFile source, final String remote) {
this.source = source;
this.remote = remote; //new RemotePath(remote, engine.getPathHelper().getPathSeparator());
}

private void upload(final TransferListener listener) throws IOException {
// This ensures "backwards compatible" behaviour.
if (source.isDirectory()) {
if (engine.getPathHelper().getComponents(remote).getName().equals(source.getName())) {
makeDirIfNotExists(remote); // Ensure that the directory exists
uploadDir(listener.directory(source.getName()), source, remote);
setAttributes(source, remote);
} else {
String adjusted = engine.getPathHelper().adjustForParent(remote, source.getName());
makeDirIfNotExists(adjusted);
uploadDir(listener.directory(source.getName()), source, adjusted);
setAttributes(source, adjusted);
}
} else if (source.isFile()) {
uploadFile(listener.file(source.getName(), source.getLength()), source, remote);
setAttributes(source, remote);
} else {
throw new IOException(source + " is not a file or directory");
}
}

private void upload(final TransferListener listener,
final LocalSourceFile local,
final String remote)
Expand All @@ -182,73 +212,63 @@ private void upload(final TransferListener listener,
adjustedPath = uploadDir(listener.directory(local.getName()), local, remote);
} else if (local.isFile()) {
adjustedPath = uploadFile(listener.file(local.getName(), local.getLength()), local, remote);
} else
} else {
throw new IOException(local + " is not a file or directory");
if (getPreserveAttributes())
engine.setAttributes(adjustedPath, getAttributes(local));
}
setAttributes(local, adjustedPath);
}

private void setAttributes(LocalSourceFile local, String remotePath) throws IOException {
if (getPreserveAttributes()) {
engine.setAttributes(remotePath, getAttributes(local));
}
}

private String uploadDir(final TransferListener listener,
final LocalSourceFile local,
final String remote)
throws IOException {
final String adjusted = prepareDir(local, remote);
makeDirIfNotExists(remote);
for (LocalSourceFile f : local.getChildren(getUploadFilter()))
upload(listener, f, adjusted);
return adjusted;
upload(listener, f, engine.getPathHelper().adjustForParent(remote, f.getName()));
return remote;
}

private String uploadFile(final StreamCopier.Listener listener,
final LocalSourceFile local,
final String remote)
throws IOException {
final String adjusted = prepareFile(local, remote);
final RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE,
OpenMode.CREAT,
OpenMode.TRUNC));
try {
final InputStream fis = local.getInputStream();
final RemoteFile.RemoteFileOutputStream rfos = rf.new RemoteFileOutputStream(0, 16);
try {
try (RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC))) {
try (InputStream fis = local.getInputStream();
RemoteFile.RemoteFileOutputStream rfos = rf.new RemoteFileOutputStream(0, 16)) {
new StreamCopier(fis, rfos)
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
.keepFlushing(false)
.listener(listener)
.copy();
} finally {
fis.close();
rfos.close();
}
} finally {
rf.close();
}
return adjusted;
}

private String prepareDir(final LocalSourceFile local, final String remote)
throws IOException {
final FileAttributes attrs;
private boolean makeDirIfNotExists(final String remote) throws IOException {
try {
attrs = engine.stat(remote);
FileAttributes attrs = engine.stat(remote);
if (attrs.getMode().getType() != FileMode.Type.DIRECTORY) {
throw new IOException(remote + " exists and should be a directory, but was a " + attrs.getMode().getType());
}
// Was not created, but existed.
return false;
} catch (SFTPException e) {
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
log.debug("probeDir: {} does not exist, creating", remote);
engine.makeDir(remote);
return remote;
return true;
} else
throw e;
}

if (attrs.getMode().getType() == FileMode.Type.DIRECTORY)
if (engine.getPathHelper().getComponents(remote).getName().equals(local.getName())) {
log.debug("probeDir: {} already exists", remote);
return remote;
} else {
log.debug("probeDir: {} already exists, path adjusted for {}", remote, local.getName());
return prepareDir(local, engine.getPathHelper().adjustForParent(remote, local.getName()));
}
else
throw new IOException(attrs.getMode().getType() + " file already exists at " + remote);
}

private String prepareFile(final LocalSourceFile local, final String remote)
Expand All @@ -264,8 +284,9 @@ private String prepareFile(final LocalSourceFile local, final String remote)
throw e;
}
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY) {
log.debug("probeFile: {} was directory, path adjusted for {}", remote, local.getName());
return engine.getPathHelper().adjustForParent(remote, local.getName());
throw new IllegalStateException();
// log.debug("probeFile: {} was directory, path adjusted for {}", remote, local.getName());
// return engine.getPathHelper().adjustForParent(remote, local.getName());
} else {
log.debug("probeFile: {} is a {} file that will be replaced", remote, attrs.getMode().getType());
return remote;
Expand All @@ -281,5 +302,37 @@ private FileAttributes getAttributes(LocalSourceFile local)
}

}

//
// private static class RemotePath {
// private String base;
// private String pathSep;
// private String relativePath;
//
// private RemotePath(String base, String pathSep) {
// this.base = base;
// this.pathSep = pathSep;
// }
//
// public RemotePath(String base, String pathSep, String relativePath) {
// this.base = base;
// this.pathSep = pathSep;
// this.relativePath = relativePath;
// }
//
// public String getRelativePath() {
// return relativePath;
// }
//
// private RemotePath subdir(String dir) {
// return new RemotePath(base, pathSep, relativePath + pathSep + dir);
// }
//
// private String filePath(String fileName) {
// return path() + pathSep + fileName;
// }
//
// private String path() {
// return base + relativePath;
// }
// }
}
71 changes: 64 additions & 7 deletions src/test/java/com/hierynomus/sshj/sftp/SFTPClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.io.File;
import java.io.IOException;

import static org.hamcrest.MatcherAssert.assertThat;

public class SFTPClientTest {

@Rule
Expand All @@ -36,19 +38,74 @@ public class SFTPClientTest {

@Test
public void shouldNotThrowExceptionOnCloseBeforeDisconnect() throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
sshClient.authPassword("test", "test");
SFTPClient sftpClient = sshClient.newSFTPClient();
File file = temp.newFile("source.txt");
FileUtil.writeToFile(file, "This is the source");

doUpload(file, temp.newFile("dest.txt"));

}

@Test
public void shouldUploadContentsToDestIfExistsAndSameNameAsSource() throws IOException {
File srcDir = temp.newFolder("toto");
File destDir = temp.newFolder("dest", "toto");
FileUtil.writeToFile(new File(srcDir, "toto.txt"), "Toto file");

doUpload(srcDir, destDir);

assertThat("dest/toto exists", destDir.exists());
assertThat("dest/toto/toto not exists", !new File(destDir, "toto").exists());
assertThat("dest/toto/toto.txt exists", new File(destDir, "toto.txt").exists());
}

@Test
public void shouldUploadIntoDestIfExistsAndDifferentNameAsSource() throws IOException {
File srcDir = temp.newFolder("toto");
File destDir = temp.newFolder("dest");
FileUtil.writeToFile(new File(srcDir, "toto.txt"), "Toto file");

doUpload(srcDir, destDir);

assertThat("dest/toto exists", destDir.exists());
assertThat("dest/toto/toto.txt exists", new File(destDir, "toto/toto.txt").exists());
}

@Test
public void shouldNotMergeSameNameSubDirs() throws IOException {
File toto = temp.newFolder("toto");
File tutu = mkdir(toto, "tutu");
File toto2 = mkdir(toto, "toto");
File dest = temp.newFolder("dest");
FileUtil.writeToFile(new File(toto, "toto.txt"), "Toto file");
FileUtil.writeToFile(new File(tutu, "tototutu.txt"), "Toto/Tutu file");
FileUtil.writeToFile(new File(toto2, "totototo.txt"), "Toto/Toto file");

doUpload(toto, dest);

assertThat("toto root should exist", new File(dest, "toto").exists());
assertThat("toto/toto.txt should exist", new File(dest, "toto/toto.txt").exists());
assertThat("toto/tutu should exist", new File(dest, "toto/tutu").exists());
assertThat("toto/tutu/tototutu.txt should exist", new File(dest, "toto/tutu/tototutu.txt").exists());
assertThat("toto/toto should exist", new File(dest, "toto/toto").exists());
assertThat("toto/toto/totototo.txt should exist", new File(dest, "toto/toto/totototo.txt").exists());
assertThat("toto/totototo.txt should not exist", !new File(dest, "totototo.txt").exists());
}

private void doUpload(File src, File dest) throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
sshClient.authPassword("test", "test");
try {
try {
sftpClient.put(file.getPath(), temp.newFile("dest.txt").getPath());
} finally {
sftpClient.close();
try (SFTPClient sftpClient = sshClient.newSFTPClient()) {
sftpClient.put(src.getPath(), dest.getPath());
}
} finally {
sshClient.disconnect();
}
}

private File mkdir(File parent, String name) {
File file = new File(parent, name);
file.mkdir();
return file;
}
}

0 comments on commit 5a64060

Please sign in to comment.