diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java new file mode 100644 index 000000000..d2fbbeccd --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java @@ -0,0 +1,213 @@ +/* + * 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 + * . + */ + +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; + +/** + * Base class for Ant tasks using jsch. + * + * @author charliehubbard76@yahoo.com + * @author riznob@hotmail.com + * @since Ant 1.6 + */ +public abstract class SSHBase extends Task implements LogListener { + + private String host; + private String keyfile; + private String knownHosts; + private boolean trust = false; + private int port = 22; + private boolean failOnError = true; + private SSHUserInfo userInfo; + + /** + * Constructor for SSHBase. + */ + public SSHBase() { + super(); + userInfo = new SSHUserInfo(); + } + + /** + * Remote host, either DNS name or IP. + * + * @param host The new host value + */ + public void setHost(String host) { + this.host = host; + } + + + public void setFailonerror( boolean failure ) { + failOnError = failure; + } + + public boolean getFailonerror() { + return failOnError; + } + + /** + * Username known to remote host. + * + * @param username The new username value + */ + public void setUsername(String username) { + userInfo.setName(username); + } + + + /** + * Sets the password for the user. + * + * @param password The new password value + */ + public void setPassword(String password) { + userInfo.setPassword(password); + } + + /** + * Sets the keyfile for the user. + * + * @param keyfile The new keyfile value + */ + public void setKeyfile(String keyfile) { + userInfo.setKeyfile(keyfile); + } + + /** + * Sets the passphrase for the users key. + * + * @param passphrase The new passphrase value + */ + public void setPassphrase(String passphrase) { + userInfo.setPassphrase(passphrase); + } + + /** + * 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 + * ${user.home}/.ssh/known_hosts. + * + * @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 ) { + userInfo.setTrust(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; + } + + public int getPort() { + return port; + } + + public void init() throws BuildException{ + super.init(); + this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts"; + this.trust = false; + this.port = 22; + } + + protected Session openSession() throws JSchException { + JSch jsch = new JSch(); + if (null != userInfo.getKeyfile()) { + jsch.addIdentity(userInfo.getKeyfile(), "passphrase"); + } + + if( knownHosts != null ) { + log( "Using known hosts: " + knownHosts, Project.MSG_DEBUG ); + jsch.setKnownHosts( knownHosts ); + } + + Session session = jsch.getSession( userInfo.getName(), host, port ); + session.setUserInfo(userInfo); + log("Connecting to " + host + ":" + port ); + session.connect(); + return session; + } + + protected SSHUserInfo getUserInfo() { + return userInfo; + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java new file mode 100644 index 000000000..e3833f2bd --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java @@ -0,0 +1,208 @@ +/* + * 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 + * . + */ + +package org.apache.tools.ant.taskdefs.optional.ssh; + +import com.jcraft.jsch.UserInfo; +import org.apache.tools.ant.Project; + +/** + * @author rhanderson + */ +public class SSHUserInfo implements UserInfo { + + private String name; + private String password = null; + private String keyfile; + private String passphrase = null; + private boolean firstTime = true; + private boolean trustAllCertificates; + + public SSHUserInfo() { + super(); + this.trustAllCertificates = true; + } + + public SSHUserInfo(String password, boolean trustAllCertificates) { + super(); + this.password = password; + this.trustAllCertificates = trustAllCertificates; + } + + /** + * @see com.jcraft.jsch.UserInfo#getName() + */ + public String getName() { + return name; + } + + /** + * @see com.jcraft.jsch.UserInfo#getPassphrase(String) + */ + public String getPassphrase(String message) { + return passphrase; + } + + /** + * @see com.jcraft.jsch.UserInfo#getPassword() + */ + public String getPassword() { + return password; + } + + /** + * @see com.jcraft.jsch.UserInfo#prompt(String) + */ + public boolean prompt(String str) { + return false; + } + + /** + * @see com.jcraft.jsch.UserInfo#retry() + */ + public boolean retry() { + return false; + } + + /** + * Sets the name. + * @param name The name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the passphrase. + * @param passphrase The passphrase to set + */ + public void setPassphrase(String passphrase) { + this.passphrase = passphrase; + } + + /** + * Sets the password. + * @param password The password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Sets the trust. + * @param boolean + */ + public void setTrust(boolean trust) { + this.trustAllCertificates = trust; + } + + /** + * Returns the passphrase. + * @return String + */ + public String getPassphrase() { + return passphrase; + } + + /** + * Returns the keyfile. + * @return String + */ + public String getKeyfile() { + return keyfile; + } + + /** + * Sets the keyfile. + * @param keyfile The keyfile to set + */ + public void setKeyfile(String keyfile) { + this.keyfile = keyfile; + } + + /** + * @see com.jcraft.jsch.UserInfo#promptPassphrase(String) + */ + public boolean promptPassphrase(String message) { + return true; + } + + /** + * @see com.jcraft.jsch.UserInfo#promptPassword(String) + */ + public boolean promptPassword( String passwordPrompt ) { + //log( passwordPrompt, Project.MSG_DEBUG ); + if( firstTime ) { + firstTime = false; + return true; + } + return firstTime; + } + + /** + * @see com.jcraft.jsch.UserInfo#promptYesNo(String) + */ + public boolean promptYesNo(String message) { + //log( prompt, Project.MSG_DEBUG ); + return trustAllCertificates; + } + + /** + * @see com.jcraft.jsch.UserInfo#showMessage(String) + */ + public void showMessage(String message) { + //log( message, Project.MSG_DEBUG ); + } + +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java index 77558ac31..463e519c0 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java @@ -74,19 +74,11 @@ import org.apache.tools.ant.types.FileSet; * @author charliehubbard76@yahoo.com * @since Ant 1.6 */ -public class Scp extends Task implements LogListener { +public class Scp extends SSHBase { 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 @@ -114,35 +106,7 @@ public class Scp extends Task implements LogListener { 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 - * ${user.home}/.ssh/known_hosts. - * @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 @@ -161,9 +125,6 @@ public class Scp extends Task implements LogListener { super.init(); this.toUri = null; this.fromUri = null; - this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts"; - this.trust = false; - this.port = 22; this.fileSets = null; } @@ -174,12 +135,13 @@ public class Scp extends Task implements LogListener { if ( fromUri == null && fileSets == null ) { throw new BuildException("Either the 'file' attribute or one " + - "FileSet is required."); + "FileSet is required."); } boolean isFromRemote = false; - if( fromUri != null ) + if( fromUri != null ) { isFromRemote = isRemoteUri(fromUri); + } boolean isToRemote = isRemoteUri(toUri); try { if (isFromRemote && !isToRemote) { @@ -194,11 +156,11 @@ public class Scp extends Task implements LogListener { // not implemented yet. } else { throw new BuildException("'todir' and 'file' attributes " + - "must have syntax like the following: " + - "user:password@host:/path"); + "must have syntax like the following: " + + "user:password@host:/path"); } } catch (Exception e) { - if( failOnError ) { + if(getFailonerror()) { throw new BuildException(e); } else { log("Caught exception: " + e.getMessage(), Project.MSG_ERR); @@ -207,20 +169,17 @@ public class Scp extends Task implements LogListener { } private void download( String fromSshUri, String toPath ) - throws JSchException, IOException { - String[] fromValues = parseUri(fromSshUri); + throws JSchException, IOException { + String file = parseUri(fromSshUri); Session session = null; try { - session = openSession(fromValues[0], - fromValues[1], - fromValues[2], - port ); + session = openSession(); ScpFromMessage message = new ScpFromMessage( session, - fromValues[3], - new File( toPath ), - fromSshUri.endsWith("*") ); - log("Receiving file: " + fromValues[3] ); + file, + new File( toPath ), + fromSshUri.endsWith("*") ); + log("Receiving file: " + file ); message.setLogListener( this ); message.execute(); } finally { @@ -230,23 +189,20 @@ public class Scp extends Task implements LogListener { } private void upload( List fileSet, String toSshUri ) - throws IOException, JSchException { - String[] toValues = parseUri(toSshUri); + throws IOException, JSchException { + String file = parseUri(toSshUri); Session session = null; try { - session = openSession( toValues[0], - toValues[1], - toValues[2], - port ); + session = openSession(); 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] ); + list, + file); message.setLogListener( this ); message.execute(); } finally { @@ -256,18 +212,15 @@ public class Scp extends Task implements LogListener { } private void upload( String fromPath, String toSshUri ) - throws IOException, JSchException { - String[] toValues = parseUri(toSshUri); + throws IOException, JSchException { + String file = parseUri(toSshUri); Session session = null; try { - session = openSession( toValues[0], - toValues[1], - toValues[2], - port ); + session = openSession(); ScpToMessage message = new ScpToMessage( session, - new File( fromPath ), - toValues[3] ); + new File( fromPath ), + file ); message.setLogListener( this ); message.execute(); } finally { @@ -276,35 +229,32 @@ public class Scp extends Task implements LogListener { } } - 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) { + private String parseUri(String uri) { int indexOfAt = uri.indexOf('@'); int indexOfColon = uri.indexOf(':'); - int indexOfPath = uri.indexOf(':', indexOfColon + 1); + if (indexOfColon > -1 && indexOfColon < indexOfAt) { + // user:password@host:/path notation + setUsername(uri.substring(0, indexOfColon)); + setPassword(uri.substring(indexOfColon + 1, indexOfAt)); + } else { + // no password, will require passphrase + setUsername(uri.substring(0, indexOfAt)); + } - 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); + if (getUserInfo().getPassword() == null + && getUserInfo().getPassphrase() == null) { + throw new BuildException("neither password nor passphrase for user " + + getUserInfo().getName() + " has been " + + "given. Can't authenticate."); + } - return values; + int indexOfPath = uri.indexOf(':', indexOfAt + 1); + if (indexOfPath == -1) { + throw new BuildException("no remote path in " + uri); + } + + setHost(uri.substring(indexOfAt + 1, indexOfPath)); + return uri.substring(indexOfPath + 1); } private boolean isRemoteUri(String uri) { @@ -338,46 +288,4 @@ public class Scp extends Task implements LogListener { 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 ); - } - } }