blob: cbaee2a308b75028a6d39b582db54dd6015ab200 [file] [log] [blame]
/*
* 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.JSchException;
import com.jcraft.jsch.Session;
import java.io.IOException;
import java.io.File;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.ArrayList;
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 SSHBase {
private String fromUri;
private String toUri;
private List fileSets = null;
/**
* 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;
}
/**
* 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.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 (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
}
}
}
private void download(String fromSshUri, String toPath)
throws JSchException, IOException {
String file = parseUri(fromSshUri);
Session session = null;
try {
session = openSession();
ScpFromMessage message =
new ScpFromMessage(session, file,
getProject().resolveFile(toPath),
fromSshUri.endsWith("*"));
log("Receiving file: " + file);
message.setLogListener(this);
message.execute();
} finally {
if (session != null) {
session.disconnect();
}
}
}
private void upload(List fileSet, String toSshUri)
throws IOException, JSchException {
String file = parseUri(toSshUri);
Session session = null;
try {
List list = new ArrayList(fileSet.size());
for (Iterator i = fileSet.iterator(); i.hasNext();) {
FileSet set = (FileSet) i.next();
Directory d = createDirectory(set);
if (d != null) {
list.add(d);
}
}
if (!list.isEmpty()) {
session = openSession();
ScpToMessage message = new ScpToMessage(session, list, file);
message.setLogListener(this);
message.execute();
}
} finally {
if (session != null) {
session.disconnect();
}
}
}
private void upload(String fromPath, String toSshUri)
throws IOException, JSchException {
String file = parseUri(toSshUri);
Session session = null;
try {
session = openSession();
ScpToMessage message =
new ScpToMessage(session, getProject().resolveFile(fromPath),
file);
message.setLogListener(this);
message.execute();
} finally {
if (session != null) {
session.disconnect();
}
}
}
private String parseUri(String uri) {
int indexOfAt = uri.indexOf('@');
int indexOfColon = uri.indexOf(':');
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));
}
if (getUserInfo().getPassword() == null
&& getUserInfo().getPassphrase() == null) {
throw new BuildException("neither password nor passphrase for user "
+ getUserInfo().getName() + " has been "
+ "given. Can't authenticate.");
}
int indexOfPath = uri.indexOf(':', indexOfAt + 1);
if (indexOfPath == -1) {
throw new BuildException("no remote path in " + uri);
}
setHost(uri.substring(indexOfAt + 1, indexOfPath));
String remotePath = uri.substring(indexOfPath + 1);
if (remotePath.equals("")) {
remotePath = ".";
}
return remotePath;
}
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(getProject());
Directory root = new Directory(scanner.getBasedir());
String[] files = scanner.getIncludedFiles();
if (files.length != 0) {
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);
}
}
}
} else {
// skip
root = null;
}
return root;
}
}