Browse Source

<scp>

Submitted by:	charliehubbard76 at yahoo dot com


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274200 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 22 years ago
parent
commit
0f276785d6
13 changed files with 1493 additions and 1 deletions
  1. +3
    -0
      WHATSNEW
  2. +22
    -1
      build.xml
  3. +133
    -0
      docs/manual/OptionalTasks/scp.html
  4. +6
    -0
      docs/manual/install.html
  5. +1
    -0
      docs/manual/optionaltasklist.html
  6. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/defaults.properties
  7. +121
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java
  8. +155
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java
  9. +59
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java
  10. +383
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
  11. +198
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
  12. +209
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java
  13. +202
    -0
      src/testcases/org/apache/tools/ant/taskdefs/optional/ssh/ScpTest.java

+ 3
- 0
WHATSNEW View File

@@ -157,6 +157,9 @@ Other changes:
* The <javadoc> task <tag> subelement has been enhanced to allow files
with tag mappings to be used.

* New task <scp> that supports file transfers over SSH. Requires
jsch, a BSD licensed SSH library that can be found at
http://www.jcraft.com/jsch/index.html

Changes from Ant 1.5.1 to Ant 1.5.2
=============================================


+ 22
- 1
build.xml View File

@@ -155,7 +155,10 @@
</or>
</selector>
<selector id="needs.jdk1.4+">
<filename name="${regexp.package}/Jdk14Regexp*"/>
<or>
<filename name="${regexp.package}/Jdk14Regexp*"/>
<filename name="${optional.package}/ssh/*"/>
</or>
</selector>

<!-- classes that should be present in Sun based JVMs, but not in
@@ -289,6 +292,9 @@
<selector id="needs.swing">
<filename name="${optional.package}/splash/*"/>
</selector>
<selector id="needs.jsch">
<filename name="${optional.package}/ssh/*"/>
</selector>
<patternset id="onlinetests">
<exclude name="**/GetTest.java" if="offline"/>
<exclude name="**/SignJarTest.java" if="offline"/>
@@ -543,6 +549,9 @@
<available property="beanshell.present"
classname="bsh.StringUtil"
classpathref="classpath"/>
<available property="jsch.present"
classname="com.jcraft.jsch.Session"
classpathref="classpath"/>
</target>


@@ -625,6 +634,7 @@
<selector refid="needs.jai" unless="jai.present"/>
<selector refid="needs.jdepend" unless="jdepend.present"/>
<selector refid="needs.swing" unless="swing.present"/>
<selector refid="needs.jsch" unless="jsch.present"/>
</or>
</not>
</selector>
@@ -792,6 +802,7 @@
<selector refid="needs.jai"/>
<selector refid="needs.jdepend"/>
<selector refid="needs.swing"/>
<selector refid="needs.jsch"/>
</or>
</not>
</and>
@@ -949,6 +960,13 @@
<selector refid="needs.weblogic.server"/>
</or>
</jar>

<jar destfile="${build.lib}/${optional.jars.prefix}-jsch.jar"
basedir="${build.classes}"
manifest="${manifest.tmp}">
<selector refid="needs.jsch"/>
</jar>

</target>

<!-- Creates jar of test utility classes -->
@@ -1616,6 +1634,9 @@
-->
<exclude name="${ant.package}/taskdefs/AbstractCvsTaskTest.java"
unless="have.cvs"/>

<!-- test needs special setup -->
<exclude name="${optional.package}/ssh/ScpTest.java"/>
</fileset>
</batchtest>
</junit>


+ 133
- 0
docs/manual/OptionalTasks/scp.html View File

@@ -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>&lt;fileset&gt;</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>
&lt;scp file=&quot;myfile.txt&quot; todir=&quot;user:password@somehost:/home/chuck&quot;/&gt;
</pre>
<p><b>Copy a single remote file to a local directory</b></p>
<pre>
&lt;scp file=&quot;user:password@somehost:/home/chuck/myfile.txt&quot; todir=&quot;../some/other/dir&quot;/&gt;
</pre>

<p><b>Copy a remote directory to a local directory</b></p>
<pre>
&lt;scp file=&quot;user:password@somehost:/home/chuck/*&quot; todir=&quot;/home/sara&quot; /&gt;
</pre>

<p><b>Copy a local directory to a remote directory</b></p>
<pre>
&lt;scp todir=&quot;user:password@somehost:/home/chuck/&quot;&gt;
&lt;fileset dir=&quot;src_dir&quot;/&gt;
&lt;/scp&gt;
</pre>
<p><b>Copy a set of files to a directory</b></p>
<pre>
&lt;scp todir=&quot;user:password@somehost:/home/chuck&quot;&gt;
&lt;fileset dir=&quot;src_dir&quot;&gt;
&lt;include name=&quot;**/*.java&quot;/&gt;
&lt;/fileset&gt;
&lt;/scp&gt;

&lt;scp todir=&quot;user:password@somehost:/home/chuck&quot;&gt;
&lt;fileset dir=&quot;src_dir&quot; excludes=&quot;**/*.java&quot;/&gt;
&lt;/scp&gt;
</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>
&lt;scp todir=&quot;${username}:${password}@host:/dir&quot; ...&gt;
</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>&lt;exec executable="scp" ... &gt;</code>
instead.
</p>

<hr><p align="center">Copyright &copy; 2003 Apache Software Foundation.
All rights Reserved.</p>

</body>
</html>


+ 6
- 0
docs/manual/install.html View File

@@ -418,6 +418,12 @@ Installing Ant / Optional Tasks</a> section above.</p>
target="_top">http://gump.covalent.net/jars/latest/xml-commons/</a>
for a nightly snapshot.</td>
</tr>
<tr>
<td>jsch.jar</td>
<td>scp task</td>
<td><a href="http://www.jcraft.com/jsch/index.html"
target="_top">http://www.jcraft.com/jsch/index.html</a></td>
</tr>
</table>
<br>
<hr>


+ 1
- 0
docs/manual/optionaltasklist.html View File

@@ -55,6 +55,7 @@
<a href="OptionalTasks/rpm.html">Rpm</a><br>
<a href="OptionalTasks/serverdeploy.html">ServerDeploy</a><br>
<a href="OptionalTasks/setproxy.html">Setproxy</a><br>
<a href="OptionalTasks/scp.html">Scp</a><br>
<a href="OptionalTasks/script.html">Script</a><br>
<a href="OptionalTasks/sound.html">Sound</a><br>
<a href="OptionalTasks/sos.html">SourceOffSite</a><br>


+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/defaults.properties View File

@@ -181,6 +181,7 @@ symlink=org.apache.tools.ant.taskdefs.optional.unix.Symlink
chgrp=org.apache.tools.ant.taskdefs.optional.unix.Chgrp
chown=org.apache.tools.ant.taskdefs.optional.unix.Chown
attrib=org.apache.tools.ant.taskdefs.optional.windows.Attrib
scp=org.apache.tools.ant.taskdefs.optional.ssh.Scp

# deprecated ant tasks (kept for back compatibility)
starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut


+ 121
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java View File

@@ -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" );
}
}

+ 155
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java View File

@@ -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();
}
}

+ 59
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java View File

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

+ 383
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java View File

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

+ 198
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java View File

@@ -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();
}
}

}

+ 209
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java View File

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

+ 202
- 0
src/testcases/org/apache/tools/ant/taskdefs/optional/ssh/ScpTest.java View File

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

Loading…
Cancel
Save