diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 328895b44..706793e99 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -236,6 +236,7 @@ Roman Ivashin
Ronen Mashal
Russell Gold
Sam Ruby
+Sandra Metz
Scott Carlson
Scott Ellsworth
Scott M. Stirling
diff --git a/WHATSNEW b/WHATSNEW
index 07abf66c4..8d15c2e31 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -168,6 +168,10 @@ Other changes:
* now supports input in a way similar to
Bugzilla report 39197.
+ * can now preserve the file modification time when downloading
+ files.
+ Bugzilla Issue 33939.
+
Changes from Ant 1.7.0 TO Ant 1.7.1
=============================================
diff --git a/contributors.xml b/contributors.xml
index cee92e86c..a9c51256e 100644
--- a/contributors.xml
+++ b/contributors.xml
@@ -955,6 +955,10 @@
Sam
Ruby
+
+ Sandra
+ Metz
+
Scott
Carlson
diff --git a/docs/manual/OptionalTasks/scp.html b/docs/manual/OptionalTasks/scp.html
index 380c89d87..6d7a43d48 100644
--- a/docs/manual/OptionalTasks/scp.html
+++ b/docs/manual/OptionalTasks/scp.html
@@ -174,6 +174,15 @@ for more information. This task has been tested with jsch-0.1.2 and later.
server that doesn't support scp1. since Ant 1.7
No; defaults to false. |
+
+ preserveLastModified |
+ Determines whether the last modification
+ timestamp of downloaded files is preserved. It only works when
+ transferring from a remote to a local system and probably doesn't
+ work with a server that doesn't support SSH2. since Ant
+ 1.8.0 |
+ No; defaults to false. |
+
Parameters specified as nested elements
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
index 878d428e6..7ba4b20e2 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
@@ -49,6 +49,7 @@ public class Scp extends SSHBase {
private String fromUri;
private String toUri;
+ private boolean preserveLastModified = false;
private List fileSets = null;
private boolean isFromRemote, isToRemote;
private boolean isSftp = false;
@@ -116,6 +117,15 @@ public class Scp extends SSHBase {
this.isToRemote = false;
}
+ /**
+ * Sets flag to determine if file timestamp from
+ * remote system is to be preserved during copy.
+ * @since Ant 1.8.0
+ */
+ public void setPreservelastmodified(boolean yesOrNo) {
+ this.preserveLastModified = yesOrNo;
+ }
+
/**
* Similiar to {@link #setTodir setTodir} but explicitly states
* that the directory is a remote.
@@ -231,12 +241,14 @@ public class Scp extends SSHBase {
message =
new ScpFromMessage(getVerbose(), session, file,
getProject().resolveFile(toPath),
- fromSshUri.endsWith("*"));
+ fromSshUri.endsWith("*"),
+ preserveLastModified);
} else {
message =
new ScpFromMessageBySftp(getVerbose(), session, file,
getProject().resolveFile(toPath),
- fromSshUri.endsWith("*"));
+ fromSshUri.endsWith("*"),
+ preserveLastModified);
}
log("Receiving file: " + file);
message.setLogListener(this);
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
index 0050400da..8e963b138 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
@@ -27,7 +27,11 @@ import java.io.FileOutputStream;
import java.io.ByteArrayOutputStream;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpATTRS;
+import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelSftp;
+import org.apache.tools.ant.util.FileUtils;
/**
* A helper object representing an scp download.
@@ -41,6 +45,7 @@ public class ScpFromMessage extends AbstractSshMessage {
private String remoteFile;
private File localFile;
private boolean isRecursive = false;
+ private boolean preserveLastModified = false;
/**
* Constructor for ScpFromMessage
@@ -74,10 +79,7 @@ public class ScpFromMessage extends AbstractSshMessage {
String aRemoteFile,
File aLocalFile,
boolean recursive) {
- super(verbose, session);
- this.remoteFile = aRemoteFile;
- this.localFile = aLocalFile;
- this.isRecursive = recursive;
+ this(false, session, aRemoteFile, aLocalFile, recursive, false);
}
/**
@@ -94,6 +96,30 @@ public class ScpFromMessage extends AbstractSshMessage {
this(false, session, aRemoteFile, aLocalFile, recursive);
}
+ /**
+ * Constructor for ScpFromMessage.
+ * @param verbose if true log extra information
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion (-r option to scp)
+ * @param preservceLastModified whether to preserve file
+ * modification times
+ * @since Ant 1.8.0
+ */
+ public ScpFromMessage(boolean verbose,
+ Session session,
+ String aRemoteFile,
+ File aLocalFile,
+ boolean recursive,
+ boolean preserveLastModified) {
+ super(verbose, session);
+ this.remoteFile = aRemoteFile;
+ this.localFile = aLocalFile;
+ this.isRecursive = recursive;
+ this.preserveLastModified = preserveLastModified;
+ }
+
/**
* Carry out the transfer.
* @throws IOException on i/o errors
@@ -123,9 +149,14 @@ public class ScpFromMessage extends AbstractSshMessage {
log("done\n");
}
+ protected boolean getPreserveLastModified() {
+ return preserveLastModified;
+ }
+
private void startRemoteCpProtocol(InputStream in,
OutputStream out,
- File localFile) throws IOException {
+ File localFile)
+ throws IOException, JSchException {
File startFile = localFile;
while (true) {
// C0644 filesize filename - header for a regular file
@@ -147,7 +178,7 @@ public class ScpFromMessage extends AbstractSshMessage {
parseAndFetchFile(serverResponse, startFile, out, in);
} else if (serverResponse.charAt(0) == 'D') {
startFile = parseAndCreateDirectory(serverResponse,
- startFile);
+ startFile);
sendAck(out);
} else if (serverResponse.charAt(0) == 'E') {
startFile = startFile.getParentFile();
@@ -178,7 +209,8 @@ public class ScpFromMessage extends AbstractSshMessage {
private void parseAndFetchFile(String serverResponse,
File localFile,
OutputStream out,
- InputStream in) throws IOException {
+ InputStream in)
+ throws IOException, JSchException {
int start = 0;
int end = serverResponse.indexOf(" ", start + 1);
start = end + 1;
@@ -197,7 +229,8 @@ public class ScpFromMessage extends AbstractSshMessage {
private void fetchFile(File localFile,
long filesize,
OutputStream out,
- InputStream in) throws IOException {
+ InputStream in)
+ throws IOException, JSchException {
byte[] buf = new byte[BUFFER_SIZE];
sendAck(out);
@@ -241,6 +274,37 @@ public class ScpFromMessage extends AbstractSshMessage {
fos.flush();
fos.close();
}
+
+ if (getPreserveLastModified()) {
+ setLastModified(localFile);
+ }
}
+ private void setLastModified(File localFile) throws JSchException {
+ SftpATTRS fileAttributes = null;
+ String remotePath = null;
+ ChannelSftp channel = openSftpChannel();
+ channel.connect();
+ try {
+ fileAttributes = channel.lstat(remoteDir(remoteFile)
+ + localFile.getName());
+ } catch (SftpException e) {
+ throw new JSchException("failed to stat remote file", e);
+ }
+ FileUtils.getFileUtils().setFileLastModified(localFile,
+ ((long) fileAttributes
+ .getMTime())
+ * 1000);
+ }
+
+ /**
+ * returns the directory part of the remote file, if any.
+ */
+ private static String remoteDir(String remoteFile) {
+ int index = remoteFile.lastIndexOf("/");
+ if (index < 0) {
+ index = remoteFile.lastIndexOf("\\");
+ }
+ return index > -1 ? remoteFile.substring(0, index + 1) : "";
+ }
}
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java
index 3669e3f2e..01a642673 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java
@@ -28,6 +28,8 @@ import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpProgressMonitor;
+import org.apache.tools.ant.util.FileUtils;
+
/**
* A helper object representing an scp download.
*/
@@ -54,11 +56,7 @@ public class ScpFromMessageBySftp extends ScpFromMessage {
String aRemoteFile,
File aLocalFile,
boolean recursive) {
- super(verbose, session);
- this.verbose = verbose;
- this.remoteFile = aRemoteFile;
- this.localFile = aLocalFile;
- this.isRecursive = recursive;
+ this(verbose, session, aRemoteFile, aLocalFile, recursive, false);
}
/**
@@ -75,6 +73,31 @@ public class ScpFromMessageBySftp extends ScpFromMessage {
this(false, session, aRemoteFile, aLocalFile, recursive);
}
+ /**
+ * Constructor for ScpFromMessageBySftp.
+ * @param verbose if true log extra information
+ * @param session the Scp session to use
+ * @param aRemoteFile the remote file name
+ * @param aLocalFile the local file
+ * @param recursive if true use recursion
+ * @param preservceLastModified whether to preserve file
+ * modification times
+ * @since Ant 1.8.0
+ */
+ public ScpFromMessageBySftp(boolean verbose,
+ Session session,
+ String aRemoteFile,
+ File aLocalFile,
+ boolean recursive,
+ boolean preserveLastModified) {
+ super(verbose, session, aRemoteFile, aLocalFile, recursive,
+ preserveLastModified);
+ this.verbose = verbose;
+ this.remoteFile = aRemoteFile;
+ this.localFile = aLocalFile;
+ this.isRecursive = recursive;
+ }
+
/**
* Carry out the transfer.
* @throws IOException on i/o errors
@@ -171,5 +194,11 @@ public class ScpFromMessageBySftp extends ScpFromMessage {
long endTime = System.currentTimeMillis();
logStats(startTime, endTime, (int) totalLength);
}
+ if (getPreserveLastModified()) {
+ FileUtils.getFileUtils().setFileLastModified(localFile,
+ ((long) le.getAttrs()
+ .getMTime())
+ * 1000);
+ }
}
}