Submitted by: charliehubbard76 at yahoo dot com git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274200 13f79535-47bb-0310-9956-ffa450edef68master
@@ -157,6 +157,9 @@ Other changes: | |||
* The <javadoc> task <tag> subelement has been enhanced to allow files | |||
with tag mappings to be used. | |||
* New task <scp> that supports file transfers over SSH. Requires | |||
jsch, a BSD licensed SSH library that can be found at | |||
http://www.jcraft.com/jsch/index.html | |||
Changes from Ant 1.5.1 to Ant 1.5.2 | |||
============================================= | |||
@@ -155,7 +155,10 @@ | |||
</or> | |||
</selector> | |||
<selector id="needs.jdk1.4+"> | |||
<filename name="${regexp.package}/Jdk14Regexp*"/> | |||
<or> | |||
<filename name="${regexp.package}/Jdk14Regexp*"/> | |||
<filename name="${optional.package}/ssh/*"/> | |||
</or> | |||
</selector> | |||
<!-- classes that should be present in Sun based JVMs, but not in | |||
@@ -289,6 +292,9 @@ | |||
<selector id="needs.swing"> | |||
<filename name="${optional.package}/splash/*"/> | |||
</selector> | |||
<selector id="needs.jsch"> | |||
<filename name="${optional.package}/ssh/*"/> | |||
</selector> | |||
<patternset id="onlinetests"> | |||
<exclude name="**/GetTest.java" if="offline"/> | |||
<exclude name="**/SignJarTest.java" if="offline"/> | |||
@@ -543,6 +549,9 @@ | |||
<available property="beanshell.present" | |||
classname="bsh.StringUtil" | |||
classpathref="classpath"/> | |||
<available property="jsch.present" | |||
classname="com.jcraft.jsch.Session" | |||
classpathref="classpath"/> | |||
</target> | |||
@@ -625,6 +634,7 @@ | |||
<selector refid="needs.jai" unless="jai.present"/> | |||
<selector refid="needs.jdepend" unless="jdepend.present"/> | |||
<selector refid="needs.swing" unless="swing.present"/> | |||
<selector refid="needs.jsch" unless="jsch.present"/> | |||
</or> | |||
</not> | |||
</selector> | |||
@@ -792,6 +802,7 @@ | |||
<selector refid="needs.jai"/> | |||
<selector refid="needs.jdepend"/> | |||
<selector refid="needs.swing"/> | |||
<selector refid="needs.jsch"/> | |||
</or> | |||
</not> | |||
</and> | |||
@@ -949,6 +960,13 @@ | |||
<selector refid="needs.weblogic.server"/> | |||
</or> | |||
</jar> | |||
<jar destfile="${build.lib}/${optional.jars.prefix}-jsch.jar" | |||
basedir="${build.classes}" | |||
manifest="${manifest.tmp}"> | |||
<selector refid="needs.jsch"/> | |||
</jar> | |||
</target> | |||
<!-- Creates jar of test utility classes --> | |||
@@ -1616,6 +1634,9 @@ | |||
--> | |||
<exclude name="${ant.package}/taskdefs/AbstractCvsTaskTest.java" | |||
unless="have.cvs"/> | |||
<!-- test needs special setup --> | |||
<exclude name="${optional.package}/ssh/ScpTest.java"/> | |||
</fileset> | |||
</batchtest> | |||
</junit> | |||
@@ -0,0 +1,133 @@ | |||
<html> | |||
<head> | |||
<meta http-equiv="Content-Language" content="en-us"> | |||
<title>SCP Task</title> | |||
</head> | |||
<body> | |||
<h2><a name="scp">SCP</a></h2> | |||
<h3>Description</h3> | |||
<p>Copies a file or FileSet to or from a remote machine running SSH daemon. | |||
FileSet <i>only</i> works for copying files from the local machine to a | |||
remote machine.</p> | |||
<p><b>Note:</b> This task depends on external libraries not included | |||
in the Ant distribution. See <a | |||
href="../install.html#librarydependencies">Library Dependencies</a> | |||
for more information.</p> | |||
<h3>Parameters</h3> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Attribute</b></td> | |||
<td valign="top"><b>Description</b></td> | |||
<td align="center" valign="top"><b>Required</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">file</td> | |||
<td valign="top">The file to copy. This can be a local path or a | |||
remote path of the form <i>user:password@host:/directory/path.</i></td> | |||
<td valign="top" align="center">Yes, unless a nested | |||
<code><fileset></code> element is used.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">todir</td> | |||
<td valign="top">The directory to copy to. This can be a local path | |||
or a remote path of the form <i>user:password@host:/directory/path</i></td> | |||
<td valian="top" align="center">Yes</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">port</td> | |||
<td valign="top">The port to connect to on the remote host.</td> | |||
<td valian="top" align="center">No, defaults to 22.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">trust</td> | |||
<td valign="top">This trusts all unknown hosts if set to yes/true.</td> | |||
<td valian="top" align="center">No, defaults to No.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">knownhosts</td> | |||
<td valign="top">This sets the known hosts file to use to validate | |||
the identity of the remote host. This must be a SSH2 format file. | |||
SSH1 format is not supported.</td> | |||
<td valian="top" align="center">No, defaults to | |||
${user.dir}/.ssh/known_hosts.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">failonerror</td> | |||
<td valign="top">Log a warning message, but do not stop the build, | |||
when the transfer does not work. | |||
</td> | |||
<td valign="top" align="center">No; defaults to true.</td> | |||
</tr> | |||
</table> | |||
<h3>Parameters specified as nested elements</h3> | |||
<h4>fileset</h4> | |||
<p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select | |||
sets of files to copy. | |||
To use a fileset, the <code>todir</code> attribute must be set.</p> | |||
<h3>Examples</h3> | |||
<p><b>Copy a single local file to a remote machine</b></p> | |||
<pre> | |||
<scp file="myfile.txt" todir="user:password@somehost:/home/chuck"/> | |||
</pre> | |||
<p><b>Copy a single remote file to a local directory</b></p> | |||
<pre> | |||
<scp file="user:password@somehost:/home/chuck/myfile.txt" todir="../some/other/dir"/> | |||
</pre> | |||
<p><b>Copy a remote directory to a local directory</b></p> | |||
<pre> | |||
<scp file="user:password@somehost:/home/chuck/*" todir="/home/sara" /> | |||
</pre> | |||
<p><b>Copy a local directory to a remote directory</b></p> | |||
<pre> | |||
<scp todir="user:password@somehost:/home/chuck/"> | |||
<fileset dir="src_dir"/> | |||
</scp> | |||
</pre> | |||
<p><b>Copy a set of files to a directory</b></p> | |||
<pre> | |||
<scp todir="user:password@somehost:/home/chuck"> | |||
<fileset dir="src_dir"> | |||
<include name="**/*.java"/> | |||
</fileset> | |||
</scp> | |||
<scp todir="user:password@somehost:/home/chuck"> | |||
<fileset dir="src_dir" excludes="**/*.java"/> | |||
</scp> | |||
</pre> | |||
<p><strong>Security Note:</strong> Hard coding passwords and/or usernames | |||
in scp task can be a serious security hole. Consider using variable | |||
substituion and include the password on the command line. For example:<br> | |||
<pre> | |||
<scp todir="${username}:${password}@host:/dir" ...> | |||
</pre> | |||
Invoke ant with the following command line: | |||
<pre> | |||
ant -Dusername=me -Dpassword=mypassword target1 target2 | |||
</pre> | |||
</p> | |||
<p><strong>Unix Note:</strong> File permissions are not retained when files | |||
are copied; they end up with the default <code>UMASK</code> permissions | |||
instead. This is caused by the lack of any means to query or set file | |||
permissions in the current Java runtimes. If you need a permission- | |||
preserving copy function, use <code><exec executable="scp" ... ></code> | |||
instead. | |||
</p> | |||
<hr><p align="center">Copyright © 2003 Apache Software Foundation. | |||
All rights Reserved.</p> | |||
</body> | |||
</html> | |||
@@ -418,6 +418,12 @@ Installing Ant / Optional Tasks</a> section above.</p> | |||
target="_top">http://gump.covalent.net/jars/latest/xml-commons/</a> | |||
for a nightly snapshot.</td> | |||
</tr> | |||
<tr> | |||
<td>jsch.jar</td> | |||
<td>scp task</td> | |||
<td><a href="http://www.jcraft.com/jsch/index.html" | |||
target="_top">http://www.jcraft.com/jsch/index.html</a></td> | |||
</tr> | |||
</table> | |||
<br> | |||
<hr> | |||
@@ -55,6 +55,7 @@ | |||
<a href="OptionalTasks/rpm.html">Rpm</a><br> | |||
<a href="OptionalTasks/serverdeploy.html">ServerDeploy</a><br> | |||
<a href="OptionalTasks/setproxy.html">Setproxy</a><br> | |||
<a href="OptionalTasks/scp.html">Scp</a><br> | |||
<a href="OptionalTasks/script.html">Script</a><br> | |||
<a href="OptionalTasks/sound.html">Sound</a><br> | |||
<a href="OptionalTasks/sos.html">SourceOffSite</a><br> | |||
@@ -181,6 +181,7 @@ symlink=org.apache.tools.ant.taskdefs.optional.unix.Symlink | |||
chgrp=org.apache.tools.ant.taskdefs.optional.unix.Chgrp | |||
chown=org.apache.tools.ant.taskdefs.optional.unix.Chown | |||
attrib=org.apache.tools.ant.taskdefs.optional.windows.Attrib | |||
scp=org.apache.tools.ant.taskdefs.optional.ssh.Scp | |||
# deprecated ant tasks (kept for back compatibility) | |||
starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut | |||
@@ -0,0 +1,121 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
import com.jcraft.jsch.Session; | |||
import com.jcraft.jsch.Channel; | |||
import com.jcraft.jsch.ChannelExec; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.InputStream; | |||
import java.text.NumberFormat; | |||
public abstract class AbstractSshMessage { | |||
protected Session session; | |||
protected LogListener listener = new LogListener() {; | |||
public void log(String message) { | |||
// do nothing; | |||
} | |||
}; | |||
public AbstractSshMessage(Session session) { | |||
this.session = session; | |||
} | |||
protected Channel openExecChannel( String command ) { | |||
ChannelExec channel = (ChannelExec) session.openChannel( "exec" ); | |||
channel.setCommand( command ); | |||
return channel; | |||
} | |||
protected void sendAck(OutputStream out) throws IOException { | |||
byte[] buf = new byte[1]; | |||
buf[0] = 0; | |||
out.write( buf ); | |||
out.flush(); | |||
} | |||
protected void waitForAck(InputStream in) throws IOException { | |||
int b = 0; | |||
do { | |||
b = in.read(); | |||
} while (b > 0); | |||
} | |||
public abstract void execute() throws IOException; | |||
public void setLogListener( LogListener aListener ) { | |||
listener = aListener; | |||
} | |||
protected void log( String message ) { | |||
listener.log( message ); | |||
} | |||
protected void logStats( long timeStarted, | |||
long timeEnded, | |||
int totalLength) { | |||
double duration = (timeEnded - timeStarted) / 1000.0; | |||
NumberFormat format = NumberFormat.getNumberInstance(); | |||
format.setMaximumFractionDigits( 2 ); | |||
format.setMinimumFractionDigits( 1 ); | |||
listener.log( "File transfer time: " + format.format( duration ) + | |||
" Average Rate: " + format.format( totalLength / duration ) + | |||
" B/s" ); | |||
} | |||
} |
@@ -0,0 +1,155 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.StringTokenizer; | |||
import java.io.File; | |||
public class Directory { | |||
private File directory; | |||
private ArrayList childDirectories; | |||
private ArrayList files; | |||
private Directory parent; | |||
public Directory( File directory ) { | |||
this( directory, null ); | |||
} | |||
public Directory( File directory , Directory parent ) { | |||
this.parent = parent; | |||
this.childDirectories = new ArrayList(); | |||
this.files = new ArrayList(); | |||
this.directory = directory; | |||
} | |||
public void addDirectory( Directory directory ) { | |||
if( !childDirectories.contains( directory ) ) | |||
childDirectories.add( directory ); | |||
} | |||
public void addFile( File file ) { | |||
files.add( file ); | |||
} | |||
public Iterator directoryIterator() { | |||
return childDirectories.iterator(); | |||
} | |||
public Iterator filesIterator() { | |||
return files.iterator(); | |||
} | |||
public Directory getParent() { | |||
return parent; | |||
} | |||
public boolean isRoot() { | |||
return parent == null; | |||
} | |||
public File getDirectory() { | |||
return directory; | |||
} | |||
public Directory getChild( File dir ) { | |||
for( int i = 0; i < childDirectories.size(); i++ ) { | |||
Directory current = (Directory) childDirectories.get(i); | |||
if( current.getDirectory().equals( dir ) ) { | |||
return current; | |||
} | |||
} | |||
return null; | |||
} | |||
public boolean equals(Object obj) { | |||
if( obj == this ) return true; | |||
if( !(obj instanceof Directory) ) return false; | |||
Directory d = (Directory)obj; | |||
return this.directory.equals( d.directory ); | |||
} | |||
public int hashCode() { | |||
return directory.hashCode(); | |||
} | |||
public String[] getPath() { | |||
return getPath( directory.getAbsolutePath() ); | |||
} | |||
public static String[] getPath( String thePath ) { | |||
StringTokenizer tokenizer = new StringTokenizer( thePath, | |||
File.separator ); | |||
String[] path = new String[ tokenizer.countTokens() ]; | |||
int i = 0; | |||
while( tokenizer.hasMoreTokens() ) { | |||
path[i] = tokenizer.nextToken(); | |||
i++; | |||
} | |||
return path; | |||
} | |||
public int fileSize() { | |||
return files.size(); | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
public interface LogListener { | |||
void log( String message ); | |||
} |
@@ -0,0 +1,383 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
import com.jcraft.jsch.*; | |||
import java.io.*; | |||
import java.util.List; | |||
import java.util.LinkedList; | |||
import java.util.Iterator; | |||
import java.util.ArrayList; | |||
import org.apache.tools.ant.Task; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.DirectoryScanner; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.types.FileSet; | |||
/** | |||
* Ant task for sending files to remote machine over ssh/scp. | |||
* | |||
* @author charliehubbard76@yahoo.com | |||
* @since Ant 1.6 | |||
*/ | |||
public class Scp extends Task implements LogListener { | |||
private String fromUri; | |||
private String toUri; | |||
private String knownHosts; | |||
private boolean trust = false; | |||
private int port = 22; | |||
private List fileSets = null; | |||
private boolean failOnError = true; | |||
public void setFailonerror( boolean failure ) { | |||
failOnError = failure; | |||
} | |||
/** | |||
* Sets the file to be transferred. This can either be a remote | |||
* file or a local file. Remote files take the form:<br> | |||
* <i>user:password@host:/directory/path/file.example</i><br> | |||
* Files to transfer can also include a wildcard to include all | |||
* files in a remote directory. For example:<br> | |||
* <i>user:password@host:/directory/path/*</i><br> | |||
* @param aFromUri a string representing the file to transfer. | |||
*/ | |||
public void setFile(String aFromUri) { | |||
this.fromUri = aFromUri; | |||
} | |||
/** | |||
* Sets the location where files will be transferred to. | |||
* This can either be a remote directory or a local directory. | |||
* Remote directories take the form of:<br> | |||
* <i>user:password@host:/directory/path/</i><br> | |||
* This parameter is required. | |||
* @param aToUri a string representing the target of the copy. | |||
*/ | |||
public void setTodir(String aToUri) { | |||
this.toUri = aToUri; | |||
} | |||
/** | |||
* Sets the path to the file that has the identities of | |||
* all known hosts. This is used by SSH protocol to validate | |||
* the identity of the host. The default is | |||
* <i>{$user.dir}/.ssh/known_hosts</i>. | |||
* @param knownHosts a path to the known hosts file. | |||
*/ | |||
public void setKnownhosts( String knownHosts ) { | |||
this.knownHosts = knownHosts; | |||
} | |||
/** | |||
* Setting this to true trusts hosts whose identity is unknown. | |||
* | |||
* @param yesOrNo if true trust the identity of unknown hosts. | |||
*/ | |||
public void setTrust( boolean yesOrNo ) { | |||
this.trust = yesOrNo; | |||
} | |||
/** | |||
* Changes the port used to connect to the remote host. | |||
* | |||
* @param port port number of remote host. | |||
*/ | |||
public void setPort( int port ) { | |||
this.port = port; | |||
} | |||
/** | |||
* Adds a FileSet tranfer to remote host. NOTE: Either | |||
* addFileSet() or setFile() are required. But, not both. | |||
* | |||
* @param set FileSet to send to remote host. | |||
*/ | |||
public void addFileset( FileSet set ) { | |||
if( fileSets == null ) { | |||
fileSets = new LinkedList(); | |||
} | |||
fileSets.add( set ); | |||
} | |||
public void init() throws BuildException { | |||
super.init(); | |||
this.toUri = null; | |||
this.fromUri = null; | |||
this.knownHosts = System.getProperty("user.dir") + "/.ssh/known_hosts"; | |||
this.trust = false; | |||
this.port = 22; | |||
this.fileSets = null; | |||
} | |||
public void execute() throws BuildException { | |||
if (toUri == null) { | |||
throw new BuildException("The 'todir' attribute is required."); | |||
} | |||
if ( fromUri == null && fileSets == null ) { | |||
throw new BuildException("Either the 'file' attribute or one " + | |||
"FileSet is required."); | |||
} | |||
boolean isFromRemote = false; | |||
if( fromUri != null ) | |||
isFromRemote = isRemoteUri(fromUri); | |||
boolean isToRemote = isRemoteUri(toUri); | |||
try { | |||
if (isFromRemote && !isToRemote) { | |||
download( fromUri, toUri ); | |||
} else if (!isFromRemote && isToRemote) { | |||
if( fileSets != null ) { | |||
upload( fileSets, toUri ); | |||
} else { | |||
upload( fromUri, toUri ); | |||
} | |||
} else if (isFromRemote && isToRemote) { | |||
// not implemented yet. | |||
} else { | |||
throw new BuildException("'todir' and 'file' attributes " + | |||
"must have syntax like the following: " + | |||
"user:password@host:/path"); | |||
} | |||
} catch (Exception e) { | |||
if( failOnError ) { | |||
throw new BuildException(e); | |||
} else { | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
private void download( String fromSshUri, String toPath ) | |||
throws JSchException, IOException { | |||
String[] fromValues = parseUri(fromSshUri); | |||
Session session = null; | |||
try { | |||
session = openSession(fromValues[0], | |||
fromValues[1], | |||
fromValues[2], | |||
port ); | |||
ScpFromMessage message = new ScpFromMessage( session, | |||
fromValues[3], | |||
new File( toPath ), | |||
fromSshUri.endsWith("*") ); | |||
log("Receiving file: " + fromValues[3] ); | |||
message.setLogListener( this ); | |||
message.execute(); | |||
} finally { | |||
if( session != null ) | |||
session.disconnect(); | |||
} | |||
} | |||
private void upload( List fileSet, String toSshUri ) | |||
throws IOException, JSchException { | |||
String[] toValues = parseUri(toSshUri); | |||
Session session = null; | |||
try { | |||
session = openSession( toValues[0], | |||
toValues[1], | |||
toValues[2], | |||
port ); | |||
List list = new ArrayList( fileSet.size() ); | |||
for( Iterator i = fileSet.iterator(); i.hasNext(); ) { | |||
FileSet set = (FileSet) i.next(); | |||
list.add( createDirectory( set ) ); | |||
} | |||
ScpToMessage message = new ScpToMessage( session, | |||
list, | |||
toValues[3] ); | |||
message.setLogListener( this ); | |||
message.execute(); | |||
} finally { | |||
if( session != null ) | |||
session.disconnect(); | |||
} | |||
} | |||
private void upload( String fromPath, String toSshUri ) | |||
throws IOException, JSchException { | |||
String[] toValues = parseUri(toSshUri); | |||
Session session = null; | |||
try { | |||
session = openSession( toValues[0], | |||
toValues[1], | |||
toValues[2], | |||
port ); | |||
ScpToMessage message = new ScpToMessage( session, | |||
new File( fromPath ), | |||
toValues[3] ); | |||
message.setLogListener( this ); | |||
message.execute(); | |||
} finally { | |||
if( session != null ) | |||
session.disconnect(); | |||
} | |||
} | |||
private Session openSession( String user, String password, | |||
String host, int port ) | |||
throws JSchException { | |||
JSch jsch = new JSch(); | |||
if( knownHosts != null ) { | |||
log( "Using known hosts: " + knownHosts, Project.MSG_DEBUG ); | |||
jsch.setKnownHosts( knownHosts ); | |||
} | |||
Session session = jsch.getSession( user, host, port ); | |||
UserInfo userInfo = new DefaultUserInfo( password, trust ); | |||
session.setUserInfo(userInfo); | |||
log("Connecting to " + host + ":" + port ); | |||
session.connect(); | |||
return session; | |||
} | |||
private String[] parseUri(String uri) { | |||
int indexOfAt = uri.indexOf('@'); | |||
int indexOfColon = uri.indexOf(':'); | |||
int indexOfPath = uri.indexOf(':', indexOfColon + 1); | |||
String[] values = new String[4]; | |||
values[0] = uri.substring(0, indexOfColon); | |||
values[1] = uri.substring(indexOfColon + 1, indexOfAt); | |||
values[2] = uri.substring(indexOfAt + 1, indexOfPath); | |||
values[3] = uri.substring(indexOfPath + 1); | |||
return values; | |||
} | |||
private boolean isRemoteUri(String uri) { | |||
boolean isRemote = true; | |||
int indexOfAt = uri.indexOf('@'); | |||
if (indexOfAt < 0) { | |||
isRemote = false; | |||
} | |||
return isRemote; | |||
} | |||
private Directory createDirectory( FileSet set ) { | |||
DirectoryScanner scanner = set.getDirectoryScanner( project ); | |||
Directory root = new Directory( scanner.getBasedir() ); | |||
String[] files = scanner.getIncludedFiles(); | |||
for (int j = 0; j < files.length; j++) { | |||
String[] path = Directory.getPath( files[j] ); | |||
Directory current = root; | |||
File currentParent = scanner.getBasedir(); | |||
for( int i = 0; i < path.length; i++ ) { | |||
File file = new File( currentParent, path[i] ); | |||
if( file.isDirectory() ) { | |||
current.addDirectory( new Directory( file ) ); | |||
current = current.getChild( file ); | |||
currentParent = current.getDirectory(); | |||
} else if( file.isFile() ) { | |||
current.addFile( file ); | |||
} | |||
} | |||
} | |||
return root; | |||
} | |||
public class DefaultUserInfo implements UserInfo { | |||
private String password = null; | |||
private boolean firstTime = true; | |||
private boolean trustAllCertificates; | |||
public DefaultUserInfo(String password, boolean trustAllCertificates) { | |||
this.password = password; | |||
this.trustAllCertificates = trustAllCertificates; | |||
} | |||
public String getPassphrase() { | |||
return null; | |||
} | |||
public String getPassword() { | |||
return password; | |||
} | |||
public boolean promptPassword( String passwordPrompt ) { | |||
log( passwordPrompt, Project.MSG_DEBUG ); | |||
if( firstTime ) { | |||
firstTime = false; | |||
return true; | |||
} | |||
return firstTime; | |||
} | |||
public boolean promptPassphrase( String passPhrasePrompt ) { | |||
return true; | |||
} | |||
public boolean promptYesNo( String prompt ) { | |||
log( prompt, Project.MSG_DEBUG ); | |||
return trustAllCertificates; | |||
} | |||
public void showMessage( String message ) { | |||
log( message, Project.MSG_DEBUG ); | |||
} | |||
} | |||
} |
@@ -0,0 +1,198 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
import com.jcraft.jsch.*; | |||
import java.io.*; | |||
import java.util.StringTokenizer; | |||
import java.text.NumberFormat; | |||
public class ScpFromMessage extends AbstractSshMessage { | |||
private String remoteFile; | |||
private File localFile; | |||
private boolean isRecursive = false; | |||
public ScpFromMessage( Session session, | |||
String aRemoteFile, | |||
File aLocalFile, | |||
boolean recursive ) { | |||
super(session); | |||
this.remoteFile = aRemoteFile; | |||
this.localFile = aLocalFile; | |||
this.isRecursive = recursive; | |||
} | |||
public void execute() throws IOException { | |||
String command = "scp -f "; | |||
if( isRecursive ) | |||
command += "-r "; | |||
command += remoteFile; | |||
Channel channel = openExecChannel( command ); | |||
try { | |||
// get I/O streams for remote scp | |||
OutputStream out = channel.getOutputStream(); | |||
InputStream in = channel.getInputStream(); | |||
channel.connect(); | |||
sendAck(out); | |||
startRemoteCpProtocol( in, out, localFile); | |||
} finally { | |||
if( channel != null ) | |||
channel.disconnect(); | |||
} | |||
log( "done\n" ); | |||
} | |||
private void startRemoteCpProtocol(InputStream in, | |||
OutputStream out, | |||
File localFile) throws IOException { | |||
File startFile = localFile; | |||
while (true) { | |||
// C0644 filesize filename - header for a regular file | |||
// T time 0 time 0\n - present if perserve time. | |||
// D directory - this is the header for a directory. | |||
ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |||
while( true ) { | |||
int read = in.read(); | |||
if( read < 0 ) return; | |||
if( (byte)read == (byte)0x0a ) break; | |||
stream.write( read ); | |||
} | |||
String serverResponse = stream.toString("UTF-8"); | |||
if( serverResponse.charAt(0) == 'C' ) { | |||
parseAndFetchFile( serverResponse, startFile, out, in ); | |||
} else if( serverResponse.charAt( 0 ) == 'D' ) { | |||
startFile = parseAndCreateDirectory( serverResponse, | |||
startFile ); | |||
sendAck( out ); | |||
} else if( serverResponse.charAt(0) == 'E' ) { | |||
startFile = startFile.getParentFile(); | |||
sendAck( out ); | |||
} else if( serverResponse.charAt( 0 ) == '\01' | |||
|| serverResponse.charAt( 0 ) == '\02' ) { | |||
// this indicates an error. | |||
throw new IOException( serverResponse.substring(1) ); | |||
} | |||
} | |||
} | |||
private File parseAndCreateDirectory(String serverResponse, | |||
File localFile) { | |||
StringTokenizer token = new StringTokenizer( serverResponse ); | |||
String command = token.nextToken(); | |||
token.nextToken(); // appears that this is not used and it's zero. | |||
String directoryName = token.nextToken(); | |||
if( localFile.isDirectory() ) { | |||
File dir = new File( localFile, directoryName ); | |||
dir.mkdir(); | |||
return dir; | |||
} | |||
return null; | |||
} | |||
private void parseAndFetchFile(String serverResponse, | |||
File localFile, | |||
OutputStream out, | |||
InputStream in) throws IOException { | |||
StringTokenizer token = new StringTokenizer( serverResponse ); | |||
String command = token.nextToken(); | |||
int filesize = Integer.parseInt( token.nextToken() ); | |||
String filename = token.nextToken(); | |||
NumberFormat formatter = NumberFormat.getIntegerInstance(); | |||
log( "Receiving: " + filename + " : " + formatter.format( filesize ) ); | |||
File transferFile = ( localFile.isDirectory() ) | |||
? new File( localFile, filename ) | |||
: localFile; | |||
fetchFile( transferFile, filesize, out, in); | |||
waitForAck(in); | |||
sendAck(out); | |||
} | |||
private void fetchFile( File localFile, | |||
int filesize, | |||
OutputStream out, | |||
InputStream in) throws IOException { | |||
byte[] buf = new byte[1024]; | |||
sendAck(out); | |||
// read a content of lfile | |||
FileOutputStream fos = new FileOutputStream( localFile ); | |||
int length; | |||
int totalLength = 0; | |||
long startTime = System.currentTimeMillis(); | |||
try { | |||
while (true) { | |||
length = in.read( buf, 0, | |||
(buf.length < filesize) ? buf.length : filesize ); | |||
if( length < 0 ) | |||
throw new EOFException("Unexpected end of stream."); | |||
fos.write( buf, 0, length ); | |||
filesize -= length; | |||
totalLength += length; | |||
if (filesize == 0) break; | |||
} | |||
} finally { | |||
long endTime = System.currentTimeMillis(); | |||
logStats( startTime, endTime, totalLength ); | |||
fos.flush(); | |||
fos.close(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,209 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
import com.jcraft.jsch.Session; | |||
import com.jcraft.jsch.Channel; | |||
import java.io.*; | |||
import java.util.*; | |||
import java.text.NumberFormat; | |||
public class ScpToMessage extends AbstractSshMessage { | |||
private File localFile; | |||
private String remotePath; | |||
private List directoryList; | |||
public ScpToMessage(Session session, | |||
File aLocalFile, | |||
String aRemotePath ) { | |||
super(session); | |||
this.localFile = aLocalFile; | |||
this.remotePath = aRemotePath; | |||
} | |||
public ScpToMessage( Session session, | |||
List aDirectoryList, | |||
String aRemotePath ) { | |||
super( session ); | |||
this.directoryList = aDirectoryList; | |||
this.remotePath = aRemotePath; | |||
} | |||
public void execute() throws IOException { | |||
if( directoryList != null ) { | |||
doMultipleTransfer(); | |||
} | |||
if( localFile != null ) { | |||
doSingleTransfer(); | |||
} | |||
log("done.\n"); | |||
} | |||
private void doSingleTransfer() throws IOException { | |||
String cmd = "scp -t " + remotePath; | |||
Channel channel = openExecChannel( cmd ); | |||
try { | |||
OutputStream out = channel.getOutputStream(); | |||
InputStream in = channel.getInputStream(); | |||
channel.connect(); | |||
waitForAck(in); | |||
sendFileToRemote(localFile, in, out); | |||
waitForAck(in); | |||
} finally { | |||
if( channel != null ) | |||
channel.disconnect(); | |||
} | |||
} | |||
private void doMultipleTransfer() throws IOException { | |||
Channel channel = openExecChannel( "scp -d -t " + remotePath ); | |||
try { | |||
OutputStream out = channel.getOutputStream(); | |||
InputStream in = channel.getInputStream(); | |||
channel.connect(); | |||
waitForAck(in); | |||
for( Iterator i = directoryList.iterator(); i.hasNext(); ) { | |||
Directory current = (Directory)i.next(); | |||
sendDirectory( current, in, out ); | |||
} | |||
waitForAck(in); | |||
} finally { | |||
if( channel != null ) | |||
channel.disconnect(); | |||
} | |||
} | |||
private void sendDirectory(Directory current, | |||
InputStream in, | |||
OutputStream out) throws IOException { | |||
for( Iterator fileIt = current.filesIterator(); fileIt.hasNext(); ) { | |||
sendFileToRemote( (File)fileIt.next(), in, out ); | |||
} | |||
for( Iterator dirIt = current.directoryIterator(); dirIt.hasNext(); ) { | |||
Directory dir = (Directory) dirIt.next(); | |||
sendDirectoryToRemote( dir, in ,out ); | |||
} | |||
} | |||
private void sendDirectoryToRemote( Directory directory, | |||
InputStream in, | |||
OutputStream out ) throws IOException { | |||
String command = "D0755 0 "; | |||
command += directory.getDirectory().getName(); | |||
command += "\n"; | |||
out.write( command.getBytes() ); | |||
out.flush(); | |||
waitForAck(in); | |||
sendDirectory( directory, in, out ); | |||
out.write( "E\n".getBytes() ); | |||
} | |||
private void sendFileToRemote( File localFile, | |||
InputStream in, | |||
OutputStream out ) throws IOException { | |||
// send "C0644 filesize filename", where filename should not include '/' | |||
int filesize = (int) localFile.length(); | |||
String command = "C0644 " + filesize + " "; | |||
command += localFile.getName(); | |||
command += "\n"; | |||
out.write( command.getBytes() ); | |||
out.flush(); | |||
waitForAck(in); | |||
// send a content of lfile | |||
FileInputStream fis = new FileInputStream(localFile); | |||
byte[] buf = new byte[1024]; | |||
long startTime = System.currentTimeMillis(); | |||
int totalLength = 0; | |||
try { | |||
NumberFormat formatter = NumberFormat.getIntegerInstance(); | |||
log( "Sending: " + localFile.getName() + " : " + | |||
formatter.format( localFile.length() ) ); | |||
while (true) { | |||
int len = fis.read(buf, 0, buf.length); | |||
if (len <= 0) break; | |||
out.write(buf, 0, len); | |||
totalLength += len; | |||
} | |||
out.flush(); | |||
sendAck(out); | |||
} finally { | |||
long endTime = System.currentTimeMillis(); | |||
logStats( startTime, endTime, totalLength ); | |||
fis.close(); | |||
} | |||
} | |||
public File getLocalFile() { | |||
return localFile; | |||
} | |||
public String getRemotePath() { | |||
return remotePath; | |||
} | |||
} |
@@ -0,0 +1,202 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2003 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "Ant" and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.ssh; | |||
import junit.framework.TestCase; | |||
import java.io.*; | |||
import java.util.List; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.taskdefs.condition.FilesMatch; | |||
import org.apache.tools.ant.types.FileSet; | |||
import org.apache.tools.ant.types.selectors.FilenameSelector; | |||
/** | |||
* This is a unit test for the Scp task in Ant. It must be | |||
* configured with command line options in order for it to work. | |||
* Here are the options: | |||
* | |||
* scp.tmp This is a local path to a temporary | |||
* directory for this task to use. | |||
* scp.host This is the remote location of the form: | |||
* "user:password@host:/path/to/directory" | |||
* scp.port The port of the listening SSH service. | |||
* Defaults to 22. (optional) | |||
* scp.known.hosts The file containing the public keys of known | |||
* hosts. Must be a SSH2 version file, but | |||
* supports RSA and DSA keys. If it is not present | |||
* this task setTrust() to true. (optional) | |||
*/ | |||
public class ScpTest extends TestCase { | |||
private File tempDir = new File( System.getProperty("scp.tmp") ); | |||
private String sshHostUri = System.getProperty("scp.host"); | |||
private int port = Integer.parseInt( System.getProperty( "scp.port", "22" ) ); | |||
private String knownHosts = System.getProperty("scp.known.hosts"); | |||
private List cleanUpList = new ArrayList(); | |||
public ScpTest(String testname) { | |||
super(testname); | |||
} | |||
protected void setUp() { | |||
cleanUpList.clear(); | |||
} | |||
protected void tearDown() { | |||
for( Iterator i = cleanUpList.iterator(); i.hasNext(); ) { | |||
File file = (File) i.next(); | |||
file.delete(); | |||
} | |||
} | |||
public void testSingleFileUploadAndDownload() throws IOException { | |||
File uploadFile = createTemporaryFile(); | |||
Scp scpTask = createTask(); | |||
scpTask.setFile( uploadFile.getPath() ); | |||
scpTask.setTodir( sshHostUri ); | |||
scpTask.execute(); | |||
File testFile = new File( tempDir.getPath() + File.separator + | |||
"download-testSingleFileUploadAndDownload.test" ); | |||
addCleanup( testFile ); | |||
assertTrue( "Assert that the testFile does not exist.", | |||
!testFile.exists() ); | |||
scpTask.setFile( sshHostUri + "/" + uploadFile.getName() ); | |||
scpTask.setTodir( testFile.getPath() ); | |||
scpTask.execute(); | |||
assertTrue( "Assert that the testFile exists.", testFile.exists() ); | |||
compareFiles( uploadFile, testFile ); | |||
} | |||
public void testMultiUploadAndDownload() throws IOException { | |||
List uploadList = new ArrayList(); | |||
for( int i = 0; i < 5; i++ ) { | |||
uploadList.add( createTemporaryFile() ); | |||
} | |||
Scp scp = createTask(); | |||
FilenameSelector selector = new FilenameSelector(); | |||
selector.setName( "scp*" ); | |||
FileSet fileset = new FileSet(); | |||
fileset.setDir( tempDir ); | |||
fileset.addFilename( selector ); | |||
scp.addFileset( fileset ); | |||
scp.setTodir( sshHostUri ); | |||
scp.execute(); | |||
File multi = new File( tempDir, "multi" ); | |||
multi.mkdir(); | |||
addCleanup( multi ); | |||
Scp scp2 = createTask(); | |||
scp2.setFile( sshHostUri + "/scp*" ); | |||
scp2.setTodir( multi.getPath() ); | |||
scp2.execute(); | |||
FilesMatch match = new FilesMatch(); | |||
for( Iterator i = uploadList.iterator(); i.hasNext(); ) { | |||
File f = (File)i.next(); | |||
match.setFile1( f ); | |||
File f2 = new File( multi, f.getName() ); | |||
match.setFile2( f2 ); | |||
assertTrue("Assert file '" + f.getPath() + "' and file '" + | |||
f2.getPath() + "'", match.eval() ); | |||
} | |||
} | |||
public void addCleanup( File file ) { | |||
cleanUpList.add( file ); | |||
} | |||
private void compareFiles(File src, File dest) { | |||
FilesMatch match = new FilesMatch(); | |||
match.setFile1( src ); | |||
match.setFile2( dest ); | |||
assertTrue( "Assert files are equal.", match.eval() ); | |||
} | |||
private File createTemporaryFile() throws IOException { | |||
File uploadFile; | |||
uploadFile = File.createTempFile( "scp", "test", tempDir ); | |||
FileWriter writer = new FileWriter( uploadFile ); | |||
writer.write("Can you hear me now?\n"); | |||
writer.close(); | |||
addCleanup( uploadFile ); | |||
return uploadFile; | |||
} | |||
private Scp createTask() { | |||
Scp scp = new Scp(); | |||
Project p = new Project(); | |||
p.init(); | |||
scp.setProject( p ); | |||
if( knownHosts != null ) { | |||
scp.setKnownhosts( knownHosts ); | |||
} else { | |||
scp.setTrust( true ); | |||
} | |||
scp.setPort( port ); | |||
return scp; | |||
} | |||
} |