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 | * The <javadoc> task <tag> subelement has been enhanced to allow files | ||||
with tag mappings to be used. | 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 | Changes from Ant 1.5.1 to Ant 1.5.2 | ||||
============================================= | ============================================= | ||||
@@ -155,7 +155,10 @@ | |||||
</or> | </or> | ||||
</selector> | </selector> | ||||
<selector id="needs.jdk1.4+"> | <selector id="needs.jdk1.4+"> | ||||
<filename name="${regexp.package}/Jdk14Regexp*"/> | |||||
<or> | |||||
<filename name="${regexp.package}/Jdk14Regexp*"/> | |||||
<filename name="${optional.package}/ssh/*"/> | |||||
</or> | |||||
</selector> | </selector> | ||||
<!-- classes that should be present in Sun based JVMs, but not in | <!-- classes that should be present in Sun based JVMs, but not in | ||||
@@ -289,6 +292,9 @@ | |||||
<selector id="needs.swing"> | <selector id="needs.swing"> | ||||
<filename name="${optional.package}/splash/*"/> | <filename name="${optional.package}/splash/*"/> | ||||
</selector> | </selector> | ||||
<selector id="needs.jsch"> | |||||
<filename name="${optional.package}/ssh/*"/> | |||||
</selector> | |||||
<patternset id="onlinetests"> | <patternset id="onlinetests"> | ||||
<exclude name="**/GetTest.java" if="offline"/> | <exclude name="**/GetTest.java" if="offline"/> | ||||
<exclude name="**/SignJarTest.java" if="offline"/> | <exclude name="**/SignJarTest.java" if="offline"/> | ||||
@@ -543,6 +549,9 @@ | |||||
<available property="beanshell.present" | <available property="beanshell.present" | ||||
classname="bsh.StringUtil" | classname="bsh.StringUtil" | ||||
classpathref="classpath"/> | classpathref="classpath"/> | ||||
<available property="jsch.present" | |||||
classname="com.jcraft.jsch.Session" | |||||
classpathref="classpath"/> | |||||
</target> | </target> | ||||
@@ -625,6 +634,7 @@ | |||||
<selector refid="needs.jai" unless="jai.present"/> | <selector refid="needs.jai" unless="jai.present"/> | ||||
<selector refid="needs.jdepend" unless="jdepend.present"/> | <selector refid="needs.jdepend" unless="jdepend.present"/> | ||||
<selector refid="needs.swing" unless="swing.present"/> | <selector refid="needs.swing" unless="swing.present"/> | ||||
<selector refid="needs.jsch" unless="jsch.present"/> | |||||
</or> | </or> | ||||
</not> | </not> | ||||
</selector> | </selector> | ||||
@@ -792,6 +802,7 @@ | |||||
<selector refid="needs.jai"/> | <selector refid="needs.jai"/> | ||||
<selector refid="needs.jdepend"/> | <selector refid="needs.jdepend"/> | ||||
<selector refid="needs.swing"/> | <selector refid="needs.swing"/> | ||||
<selector refid="needs.jsch"/> | |||||
</or> | </or> | ||||
</not> | </not> | ||||
</and> | </and> | ||||
@@ -949,6 +960,13 @@ | |||||
<selector refid="needs.weblogic.server"/> | <selector refid="needs.weblogic.server"/> | ||||
</or> | </or> | ||||
</jar> | </jar> | ||||
<jar destfile="${build.lib}/${optional.jars.prefix}-jsch.jar" | |||||
basedir="${build.classes}" | |||||
manifest="${manifest.tmp}"> | |||||
<selector refid="needs.jsch"/> | |||||
</jar> | |||||
</target> | </target> | ||||
<!-- Creates jar of test utility classes --> | <!-- Creates jar of test utility classes --> | ||||
@@ -1616,6 +1634,9 @@ | |||||
--> | --> | ||||
<exclude name="${ant.package}/taskdefs/AbstractCvsTaskTest.java" | <exclude name="${ant.package}/taskdefs/AbstractCvsTaskTest.java" | ||||
unless="have.cvs"/> | unless="have.cvs"/> | ||||
<!-- test needs special setup --> | |||||
<exclude name="${optional.package}/ssh/ScpTest.java"/> | |||||
</fileset> | </fileset> | ||||
</batchtest> | </batchtest> | ||||
</junit> | </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> | target="_top">http://gump.covalent.net/jars/latest/xml-commons/</a> | ||||
for a nightly snapshot.</td> | for a nightly snapshot.</td> | ||||
</tr> | </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> | </table> | ||||
<br> | <br> | ||||
<hr> | <hr> | ||||
@@ -55,6 +55,7 @@ | |||||
<a href="OptionalTasks/rpm.html">Rpm</a><br> | <a href="OptionalTasks/rpm.html">Rpm</a><br> | ||||
<a href="OptionalTasks/serverdeploy.html">ServerDeploy</a><br> | <a href="OptionalTasks/serverdeploy.html">ServerDeploy</a><br> | ||||
<a href="OptionalTasks/setproxy.html">Setproxy</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/script.html">Script</a><br> | ||||
<a href="OptionalTasks/sound.html">Sound</a><br> | <a href="OptionalTasks/sound.html">Sound</a><br> | ||||
<a href="OptionalTasks/sos.html">SourceOffSite</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 | chgrp=org.apache.tools.ant.taskdefs.optional.unix.Chgrp | ||||
chown=org.apache.tools.ant.taskdefs.optional.unix.Chown | chown=org.apache.tools.ant.taskdefs.optional.unix.Chown | ||||
attrib=org.apache.tools.ant.taskdefs.optional.windows.Attrib | 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) | # deprecated ant tasks (kept for back compatibility) | ||||
starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut | 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; | |||||
} | |||||
} |