GH-3974: Fix SftpSession for absolute paths
Fixes https://github.com/spring-projects/spring-integration/issues/3974 * Treat a leading `/` as an indicator of absolute path request in the `SftpSession.doList()` * Call `sftpClient.canonicalPath()` for path without a leading `/` to resolve it as relative path in the user home * Add a note in docs for `LS` command
This commit is contained in:
@@ -78,7 +78,7 @@ public class SftpSession implements Session<SftpClient.DirEntry> {
|
||||
}
|
||||
|
||||
public Stream<SftpClient.DirEntry> doList(String path) throws IOException {
|
||||
String remotePath = StringUtils.trimTrailingCharacter(StringUtils.trimLeadingCharacter(path, '/'), '/');
|
||||
String remotePath = StringUtils.trimTrailingCharacter(path, '/');
|
||||
String remoteDir = remotePath;
|
||||
int lastIndex = remotePath.lastIndexOf('/');
|
||||
if (lastIndex > 0) {
|
||||
@@ -96,7 +96,10 @@ public class SftpSession implements Session<SftpClient.DirEntry> {
|
||||
remoteDir = remotePath;
|
||||
}
|
||||
}
|
||||
remoteDir = remoteDir.length() == 0 ? this.sftpClient.canonicalPath("") : remoteDir;
|
||||
remoteDir =
|
||||
remoteDir.length() > 0 && remoteDir.charAt(0) == '/'
|
||||
? remoteDir
|
||||
: this.sftpClient.canonicalPath(remoteDir);
|
||||
return StreamSupport.stream(this.sftpClient.readDir(remoteDir).spliterator(), false)
|
||||
.filter((entry) -> !isPattern || PatternMatchUtils.simpleMatch(remoteFile, entry.getFilename()));
|
||||
}
|
||||
|
||||
@@ -189,13 +189,14 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
|
||||
try {
|
||||
SftpClient sftpClient = mock(SftpClient.class);
|
||||
when(sftpClient.canonicalPath("remote-test-dir")).thenReturn("/remote-test-dir");
|
||||
|
||||
String[] files = new File("remote-test-dir").list();
|
||||
for (String fileName : files) {
|
||||
when(sftpClient.read("remote-test-dir/" + fileName))
|
||||
.thenReturn(new FileInputStream("remote-test-dir/" + fileName));
|
||||
}
|
||||
when(sftpClient.readDir("remote-test-dir")).thenReturn(this.sftpEntries);
|
||||
when(sftpClient.readDir("/remote-test-dir")).thenReturn(this.sftpEntries);
|
||||
|
||||
return SftpTestSessionFactory.createSftpSession(sftpClient);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class SftpMessageSourceTests extends SftpTestSupport {
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testMaxFetch() throws Exception {
|
||||
public void testMaxFetch() {
|
||||
SftpInboundFileSynchronizingMessageSource messageSource = buildSource();
|
||||
Message<?> received = messageSource.receive();
|
||||
assertThat(received).isNotNull();
|
||||
@@ -52,9 +52,9 @@ public class SftpMessageSourceTests extends SftpTestSupport {
|
||||
.isIn(" sftpSource1.txt", "sftpSource2.txt");
|
||||
}
|
||||
|
||||
private SftpInboundFileSynchronizingMessageSource buildSource() throws Exception {
|
||||
private SftpInboundFileSynchronizingMessageSource buildSource() {
|
||||
SftpInboundFileSynchronizer sync = new SftpInboundFileSynchronizer(sessionFactory());
|
||||
sync.setRemoteDirectory("sftpSource/");
|
||||
sync.setRemoteDirectory("/sftpSource/");
|
||||
sync.setBeanFactory(this.context);
|
||||
SftpInboundFileSynchronizingMessageSource messageSource = new SftpInboundFileSynchronizingMessageSource(sync);
|
||||
messageSource.setLocalDirectory(getTargetLocalDirectory());
|
||||
|
||||
@@ -174,14 +174,14 @@ public class SftpOutboundTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test //INT-2275
|
||||
@Test
|
||||
public void testFtpOutboundGatewayInsideChain() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"SftpOutboundInsideChainTests-context.xml", getClass());
|
||||
|
||||
MessageChannel channel = context.getBean("outboundGatewayInsideChain", MessageChannel.class);
|
||||
|
||||
channel.send(MessageBuilder.withPayload("remote-test-dir").build());
|
||||
channel.send(MessageBuilder.withPayload("/remote-test-dir").build());
|
||||
|
||||
PollableChannel output = context.getBean("replyChannel", PollableChannel.class);
|
||||
|
||||
@@ -308,7 +308,7 @@ public class SftpOutboundTests {
|
||||
Arrays.stream(files)
|
||||
.map((file) -> new SftpClient.DirEntry(file, file, new SftpClient.Attributes()))
|
||||
.toList();
|
||||
when(sftpClient.readDir("remote-test-dir")).thenReturn(dirEntries);
|
||||
when(sftpClient.readDir("/remote-test-dir")).thenReturn(dirEntries);
|
||||
|
||||
return SftpTestSessionFactory.createSftpSession(sftpClient);
|
||||
}
|
||||
|
||||
@@ -945,6 +945,8 @@ When using the recursive option (`-R`), the `fileName` includes any subdirectory
|
||||
If you use the `-dirs` option, each recursive directory is also returned as an element in the list.
|
||||
In this case, we recommend that you not use the `-1` option, because you would not be able to distinguish files from directories, which you can do when you use `FileInfo` objects.
|
||||
|
||||
If remote path to list starts with a `/` symbol, it is treated by SFTP as an absolute path; without - as a relative path in the current user home.
|
||||
|
||||
==== Using `nlst` Command
|
||||
|
||||
Version 5 introduced support for the `nlst` command.
|
||||
@@ -1341,7 +1343,7 @@ Currently, supported events are:
|
||||
* `PathRemovedEvent` - a file or directory was removed
|
||||
* `SessionClosedEvent` - the client has disconnected
|
||||
|
||||
Each of these is a subclass of `ApacheMinaSftpEvent`; you can configure a single listener to receive all of the event types.
|
||||
Each of these is a subclass of `ApacheMinaSftpEvent`; you can configure a single listener to receive all the event types.
|
||||
The `source` property of each event is a `ServerSession`, from which you can obtain information such as the client address; a convenient `getSession()` method is provided on the abstract event.
|
||||
|
||||
To configure the server with the listener (which must be a Spring bean), simply add it to the `SftpSubsystemFactory`:
|
||||
|
||||
Reference in New Issue
Block a user