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; | |||||
| } | |||||
| } | |||||