bodewig     2003/03/06 04:42:44

  Modified:    .        WHATSNEW build.xml
               docs/manual install.html optionaltasklist.html
               src/main/org/apache/tools/ant/taskdefs defaults.properties
  Added:       docs/manual/OptionalTasks scp.html
               src/main/org/apache/tools/ant/taskdefs/optional/ssh
                        AbstractSshMessage.java Directory.java
                        LogListener.java Scp.java ScpFromMessage.java
                        ScpToMessage.java
               src/testcases/org/apache/tools/ant/taskdefs/optional/ssh
                        ScpTest.java
  Log:
  <scp>
  
  Submitted by: charliehubbard76 at yahoo dot com
  
  Revision  Changes    Path
  1.361     +3 -0      ant/WHATSNEW
  
  Index: WHATSNEW
  ===================================================================
  RCS file: /home/cvs/ant/WHATSNEW,v
  retrieving revision 1.360
  retrieving revision 1.361
  diff -u -r1.360 -r1.361
  --- WHATSNEW  6 Mar 2003 12:04:59 -0000       1.360
  +++ WHATSNEW  6 Mar 2003 12:42:43 -0000       1.361
  @@ -157,6 +157,9 @@
   * 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
   =============================================
  
  
  
  1.354     +22 -1     ant/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/ant/build.xml,v
  retrieving revision 1.353
  retrieving revision 1.354
  diff -u -r1.353 -r1.354
  --- build.xml 26 Feb 2003 13:06:06 -0000      1.353
  +++ build.xml 6 Mar 2003 12:42:43 -0000       1.354
  @@ -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>
  
  
  
  1.47      +6 -0      ant/docs/manual/install.html
  
  Index: install.html
  ===================================================================
  RCS file: /home/cvs/ant/docs/manual/install.html,v
  retrieving revision 1.46
  retrieving revision 1.47
  diff -u -r1.46 -r1.47
  --- install.html      24 Feb 2003 08:25:19 -0000      1.46
  +++ install.html      6 Mar 2003 12:42:43 -0000       1.47
  @@ -418,6 +418,12 @@
       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.35      +1 -0      ant/docs/manual/optionaltasklist.html
  
  Index: optionaltasklist.html
  ===================================================================
  RCS file: /home/cvs/ant/docs/manual/optionaltasklist.html,v
  retrieving revision 1.34
  retrieving revision 1.35
  diff -u -r1.34 -r1.35
  --- optionaltasklist.html     7 Nov 2002 16:26:18 -0000       1.34
  +++ optionaltasklist.html     6 Mar 2003 12:42:43 -0000       1.35
  @@ -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.1                  ant/docs/manual/OptionalTasks/scp.html
  
  Index: scp.html
  ===================================================================
  <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:[EMAIL 
PROTECTED]:/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:[EMAIL 
PROTECTED]:/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:[EMAIL 
PROTECTED]:/home/chuck&quot;/&gt;
  </pre>
  <p><b>Copy a single remote file to a local directory</b></p>
  <pre>
    &lt;scp file=&quot;user:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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}:[EMAIL PROTECTED]:/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>
  
  
  
  
  1.140     +1 -0      
ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties
  
  Index: defaults.properties
  ===================================================================
  RCS file: 
/home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties,v
  retrieving revision 1.139
  retrieving revision 1.140
  diff -u -r1.139 -r1.140
  --- defaults.properties       4 Feb 2003 12:23:50 -0000       1.139
  +++ defaults.properties       6 Mar 2003 12:42:44 -0000       1.140
  @@ -181,6 +181,7 @@
   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
  
  
  
  1.1                  
ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java
  
  Index: AbstractSshMessage.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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" );
      }
  }
  
  
  
  1.1                  
ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java
  
  Index: Directory.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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();
      }
  }
  
  
  
  1.1                  
ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java
  
  Index: LogListener.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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 );
  }
  
  
  
  1.1                  
ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
  
  Index: Scp.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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 [EMAIL PROTECTED]
   * @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:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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:[EMAIL PROTECTED]:/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 );
          }
      }
  }
  
  
  
  1.1                  
ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
  
  Index: ScpFromMessage.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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();
          }
      }
  
  }
  
  
  
  1.1                  
ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java
  
  Index: ScpToMessage.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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;
      }
  }
  
  
  
  1.1                  
ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ssh/ScpTest.java
  
  Index: ScpTest.java
  ===================================================================
  /*
   * 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 [EMAIL PROTECTED]
   *
   * 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:[EMAIL PROTECTED]:/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;
      }
  }
  
  
  

Reply via email to