001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs.optional.ssh;
020:
021: import com.jcraft.jsch.JSchException;
022: import com.jcraft.jsch.Session;
023:
024: import java.io.IOException;
025: import java.io.File;
026:
027: import java.util.List;
028: import java.util.LinkedList;
029: import java.util.Iterator;
030: import java.util.ArrayList;
031:
032: import org.apache.tools.ant.BuildException;
033: import org.apache.tools.ant.DirectoryScanner;
034: import org.apache.tools.ant.Project;
035: import org.apache.tools.ant.types.FileSet;
036:
037: /**
038: * Ant task for sending files to remote machine over ssh/scp.
039: *
040: * @since Ant 1.6
041: */
042: public class Scp extends SSHBase {
043:
044: private static final String[] FROM_ATTRS = { "file", "localfile",
045: "remotefile" };
046:
047: private static final String[] TO_ATTRS = { "todir", "localtodir",
048: "remotetodir", "localtofile", "remotetofile" };
049:
050: private String fromUri;
051: private String toUri;
052: private List fileSets = null;
053: private boolean isFromRemote, isToRemote;
054: private boolean isSftp = false;
055:
056: /**
057: * Sets the file to be transferred. This can either be a remote
058: * file or a local file. Remote files take the form:<br>
059: * <i>user:password@host:/directory/path/file.example</i><br>
060: * Files to transfer can also include a wildcard to include all
061: * files in a remote directory. For example:<br>
062: * <i>user:password@host:/directory/path/*</i><br>
063: * @param aFromUri a string representing the file to transfer.
064: */
065: public void setFile(String aFromUri) {
066: setFromUri(aFromUri);
067: this .isFromRemote = isRemoteUri(this .fromUri);
068: }
069:
070: /**
071: * Sets the location where files will be transferred to.
072: * This can either be a remote directory or a local directory.
073: * Remote directories take the form of:<br>
074: * <i>user:password@host:/directory/path/</i><br>
075: * This parameter is required.
076:
077: * @param aToUri a string representing the target of the copy.
078: */
079: public void setTodir(String aToUri) {
080: setToUri(aToUri);
081: this .isToRemote = isRemoteUri(this .toUri);
082: }
083:
084: /**
085: * Similiar to {@link #setFile setFile} but explicitly states that
086: * the file is a local file. This is the only way to specify a
087: * local file with a @ character.
088: * @param aFromUri a string representing the source of the copy.
089: * @since Ant 1.6.2
090: */
091: public void setLocalFile(String aFromUri) {
092: setFromUri(aFromUri);
093: this .isFromRemote = false;
094: }
095:
096: /**
097: * Similiar to {@link #setFile setFile} but explicitly states that
098: * the file is a remote file.
099: * @param aFromUri a string representing the source of the copy.
100: * @since Ant 1.6.2
101: */
102: public void setRemoteFile(String aFromUri) {
103: setFromUri(aFromUri);
104: this .isFromRemote = true;
105: }
106:
107: /**
108: * Similiar to {@link #setTodir setTodir} but explicitly states
109: * that the directory is a local. This is the only way to specify
110: * a local directory with a @ character.
111: * @param aToUri a string representing the target of the copy.
112: * @since Ant 1.6.2
113: */
114: public void setLocalTodir(String aToUri) {
115: setToUri(aToUri);
116: this .isToRemote = false;
117: }
118:
119: /**
120: * Similiar to {@link #setTodir setTodir} but explicitly states
121: * that the directory is a remote.
122: * @param aToUri a string representing the target of the copy.
123: * @since Ant 1.6.2
124: */
125: public void setRemoteTodir(String aToUri) {
126: setToUri(aToUri);
127: this .isToRemote = true;
128: }
129:
130: /**
131: * Changes the file name to the given name while receiving it,
132: * only useful if receiving a single file.
133: * @param aToUri a string representing the target of the copy.
134: * @since Ant 1.6.2
135: */
136: public void setLocalTofile(String aToUri) {
137: setToUri(aToUri);
138: this .isToRemote = false;
139: }
140:
141: /**
142: * Changes the file name to the given name while sending it,
143: * only useful if sending a single file.
144: * @param aToUri a string representing the target of the copy.
145: * @since Ant 1.6.2
146: */
147: public void setRemoteTofile(String aToUri) {
148: setToUri(aToUri);
149: this .isToRemote = true;
150: }
151:
152: /**
153: * Setting this to true to use sftp protocol.
154: *
155: * @param yesOrNo if true sftp protocol will be used.
156: */
157: public void setSftp(boolean yesOrNo) {
158: isSftp = yesOrNo;
159: }
160:
161: /**
162: * Adds a FileSet tranfer to remote host. NOTE: Either
163: * addFileSet() or setFile() are required. But, not both.
164: *
165: * @param set FileSet to send to remote host.
166: */
167: public void addFileset(FileSet set) {
168: if (fileSets == null) {
169: fileSets = new LinkedList();
170: }
171: fileSets.add(set);
172: }
173:
174: /**
175: * Initialize this task.
176: * @throws BuildException on error
177: */
178: public void init() throws BuildException {
179: super .init();
180: this .toUri = null;
181: this .fromUri = null;
182: this .fileSets = null;
183: }
184:
185: /**
186: * Execute this task.
187: * @throws BuildException on error
188: */
189: public void execute() throws BuildException {
190: if (toUri == null) {
191: throw exactlyOne(TO_ATTRS);
192: }
193: if (fromUri == null && fileSets == null) {
194: throw exactlyOne(FROM_ATTRS, "one or more nested filesets");
195: }
196: try {
197: if (isFromRemote && !isToRemote) {
198: download(fromUri, toUri);
199: } else if (!isFromRemote && isToRemote) {
200: if (fileSets != null) {
201: upload(fileSets, toUri);
202: } else {
203: upload(fromUri, toUri);
204: }
205: } else if (isFromRemote && isToRemote) {
206: throw new BuildException(
207: "Copying from a remote server to a remote server is not supported.");
208: } else {
209: throw new BuildException(
210: "'todir' and 'file' attributes "
211: + "must have syntax like the following: "
212: + "user:password@host:/path");
213: }
214: } catch (Exception e) {
215: if (getFailonerror()) {
216: throw new BuildException(e);
217: } else {
218: log("Caught exception: " + e.getMessage(),
219: Project.MSG_ERR);
220: }
221: }
222: }
223:
224: private void download(String fromSshUri, String toPath)
225: throws JSchException, IOException {
226: String file = parseUri(fromSshUri);
227:
228: Session session = null;
229: try {
230: session = openSession();
231: ScpFromMessage message = null;
232: if (!isSftp) {
233: message = new ScpFromMessage(getVerbose(), session,
234: file, getProject().resolveFile(toPath),
235: fromSshUri.endsWith("*"));
236: } else {
237: message = new ScpFromMessageBySftp(getVerbose(),
238: session, file,
239: getProject().resolveFile(toPath), fromSshUri
240: .endsWith("*"));
241: }
242: log("Receiving file: " + file);
243: message.setLogListener(this );
244: message.execute();
245: } finally {
246: if (session != null) {
247: session.disconnect();
248: }
249: }
250: }
251:
252: private void upload(List fileSet, String toSshUri)
253: throws IOException, JSchException {
254: String file = parseUri(toSshUri);
255:
256: Session session = null;
257: try {
258: List list = new ArrayList(fileSet.size());
259: for (Iterator i = fileSet.iterator(); i.hasNext();) {
260: FileSet set = (FileSet) i.next();
261: Directory d = createDirectory(set);
262: if (d != null) {
263: list.add(d);
264: }
265: }
266: if (!list.isEmpty()) {
267: session = openSession();
268: ScpToMessage message = null;
269: if (!isSftp) {
270: message = new ScpToMessage(getVerbose(), session,
271: list, file);
272: } else {
273: message = new ScpToMessageBySftp(getVerbose(),
274: session, list, file);
275: }
276: message.setLogListener(this );
277: message.execute();
278: }
279: } finally {
280: if (session != null) {
281: session.disconnect();
282: }
283: }
284: }
285:
286: private void upload(String fromPath, String toSshUri)
287: throws IOException, JSchException {
288: String file = parseUri(toSshUri);
289:
290: Session session = null;
291: try {
292: session = openSession();
293: ScpToMessage message = null;
294: if (!isSftp) {
295: message = new ScpToMessage(getVerbose(), session,
296: getProject().resolveFile(fromPath), file);
297: } else {
298: message = new ScpToMessageBySftp(getVerbose(), session,
299: getProject().resolveFile(fromPath), file);
300: }
301: message.setLogListener(this );
302: message.execute();
303: } finally {
304: if (session != null) {
305: session.disconnect();
306: }
307: }
308: }
309:
310: private String parseUri(String uri) {
311: int indexOfAt = uri.lastIndexOf('@');
312: int indexOfColon = uri.indexOf(':');
313: if (indexOfColon > -1 && indexOfColon < indexOfAt) {
314: // user:password@host:/path notation
315: setUsername(uri.substring(0, indexOfColon));
316: setPassword(uri.substring(indexOfColon + 1, indexOfAt));
317: } else {
318: // no password, will require passphrase
319: setUsername(uri.substring(0, indexOfAt));
320: }
321:
322: if (getUserInfo().getPassword() == null
323: && getUserInfo().getPassphrase() == null) {
324: throw new BuildException(
325: "neither password nor passphrase for user "
326: + getUserInfo().getName() + " has been "
327: + "given. Can't authenticate.");
328: }
329:
330: int indexOfPath = uri.indexOf(':', indexOfAt + 1);
331: if (indexOfPath == -1) {
332: throw new BuildException("no remote path in " + uri);
333: }
334:
335: setHost(uri.substring(indexOfAt + 1, indexOfPath));
336: String remotePath = uri.substring(indexOfPath + 1);
337: if (remotePath.equals("")) {
338: remotePath = ".";
339: }
340: return remotePath;
341: }
342:
343: private boolean isRemoteUri(String uri) {
344: boolean isRemote = true;
345: int indexOfAt = uri.indexOf('@');
346: if (indexOfAt < 0) {
347: isRemote = false;
348: }
349: return isRemote;
350: }
351:
352: private Directory createDirectory(FileSet set) {
353: DirectoryScanner scanner = set
354: .getDirectoryScanner(getProject());
355: Directory root = new Directory(scanner.getBasedir());
356: String[] files = scanner.getIncludedFiles();
357: if (files.length != 0) {
358: for (int j = 0; j < files.length; j++) {
359: String[] path = Directory.getPath(files[j]);
360: Directory current = root;
361: File currentParent = scanner.getBasedir();
362: for (int i = 0; i < path.length; i++) {
363: File file = new File(currentParent, path[i]);
364: if (file.isDirectory()) {
365: current.addDirectory(new Directory(file));
366: current = current.getChild(file);
367: currentParent = current.getDirectory();
368: } else if (file.isFile()) {
369: current.addFile(file);
370: }
371: }
372: }
373: } else {
374: // skip
375: root = null;
376: }
377: return root;
378: }
379:
380: private void setFromUri(String fromUri) {
381: if (this .fromUri != null) {
382: throw exactlyOne(FROM_ATTRS);
383: }
384: this .fromUri = fromUri;
385: }
386:
387: private void setToUri(String toUri) {
388: if (this .toUri != null) {
389: throw exactlyOne(TO_ATTRS);
390: }
391: this .toUri = toUri;
392: }
393:
394: private BuildException exactlyOne(String[] attrs) {
395: return exactlyOne(attrs, null);
396: }
397:
398: private BuildException exactlyOne(String[] attrs, String alt) {
399: StringBuffer buf = new StringBuffer("Exactly one of ").append(
400: '[').append(attrs[0]);
401: for (int i = 1; i < attrs.length; i++) {
402: buf.append('|').append(attrs[i]);
403: }
404: buf.append(']');
405: if (alt != null) {
406: buf.append(" or ").append(alt);
407: }
408: return new BuildException(buf.append(" is required.")
409: .toString());
410: }
411: }
|