1. /*
  2. * Copyright 2003-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.ssh;
  18. import com.jcraft.jsch.JSchException;
  19. import com.jcraft.jsch.Session;
  20. import java.io.IOException;
  21. import java.io.File;
  22. import java.util.List;
  23. import java.util.LinkedList;
  24. import java.util.Iterator;
  25. import java.util.ArrayList;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.DirectoryScanner;
  28. import org.apache.tools.ant.Project;
  29. import org.apache.tools.ant.types.FileSet;
  30. /**
  31. * Ant task for sending files to remote machine over ssh/scp.
  32. *
  33. * @since Ant 1.6
  34. */
  35. public class Scp extends SSHBase {
  36. private String fromUri;
  37. private String toUri;
  38. private List fileSets = null;
  39. private boolean isFromRemote, isToRemote;
  40. /**
  41. * Sets the file to be transferred. This can either be a remote
  42. * file or a local file. Remote files take the form:<br>
  43. * <i>user:password@host:/directory/path/file.example</i><br>
  44. * Files to transfer can also include a wildcard to include all
  45. * files in a remote directory. For example:<br>
  46. * <i>user:password@host:/directory/path/*</i><br>
  47. * @param aFromUri a string representing the file to transfer.
  48. */
  49. public void setFile(String aFromUri) {
  50. this.fromUri = aFromUri;
  51. this.isFromRemote = isRemoteUri(this.fromUri);
  52. }
  53. /**
  54. * Sets the location where files will be transferred to.
  55. * This can either be a remote directory or a local directory.
  56. * Remote directories take the form of:<br>
  57. * <i>user:password@host:/directory/path/</i><br>
  58. * This parameter is required.
  59. * @param aToUri a string representing the target of the copy.
  60. */
  61. public void setTodir(String aToUri) {
  62. this.toUri = aToUri;
  63. this.isToRemote = isRemoteUri(this.toUri);
  64. }
  65. /**
  66. * Similiar to {@link #setFile setFile} but explicitly states that
  67. * the file is a local file. This is the only way to specify a
  68. * local file with a @ character.
  69. * @since Ant 1.6.2
  70. */
  71. public void setLocalFile(String aFromUri) {
  72. this.fromUri = aFromUri;
  73. this.isFromRemote = false;
  74. }
  75. /**
  76. * Similiar to {@link #setFile setFile} but explicitly states that
  77. * the file is a remote file.
  78. * @since Ant 1.6.2
  79. */
  80. public void setRemoteFile(String aFromUri) {
  81. this.fromUri = aFromUri;
  82. this.isFromRemote = true;
  83. }
  84. /**
  85. * Similiar to {@link #setTodir setTodir} but explicitly states
  86. * that the directory is a local. This is the only way to specify
  87. * a local directory with a @ character.
  88. * @since Ant 1.6.2
  89. */
  90. public void setLocalTodir(String aToUri) {
  91. this.toUri = aToUri;
  92. this.isToRemote = false;
  93. }
  94. /**
  95. * Similiar to {@link #setTodir setTodir} but explicitly states
  96. * that the directory is a remote.
  97. * @since Ant 1.6.2
  98. */
  99. public void setRemoteTodir(String aToUri) {
  100. this.toUri = aToUri;
  101. this.isToRemote = true;
  102. }
  103. /**
  104. * Changes the file name to the given name while receiving it,
  105. * only useful if receiving a single file.
  106. * @since Ant 1.6.2
  107. */
  108. public void setLocalTofile(String aToUri) {
  109. this.toUri = aToUri;
  110. this.isToRemote = false;
  111. }
  112. /**
  113. * Changes the file name to the given name while sending it,
  114. * only useful if sending a single file.
  115. * @since Ant 1.6.2
  116. */
  117. public void setRemoteTofile(String aToUri) {
  118. this.toUri = aToUri;
  119. this.isToRemote = true;
  120. }
  121. /**
  122. * Adds a FileSet tranfer to remote host. NOTE: Either
  123. * addFileSet() or setFile() are required. But, not both.
  124. *
  125. * @param set FileSet to send to remote host.
  126. */
  127. public void addFileset(FileSet set) {
  128. if (fileSets == null) {
  129. fileSets = new LinkedList();
  130. }
  131. fileSets.add(set);
  132. }
  133. public void init() throws BuildException {
  134. super.init();
  135. this.toUri = null;
  136. this.fromUri = null;
  137. this.fileSets = null;
  138. }
  139. public void execute() throws BuildException {
  140. if (toUri == null) {
  141. throw new BuildException("Either 'todir' or 'tofile' attribute "
  142. + "is required.");
  143. }
  144. if (fromUri == null && fileSets == null) {
  145. throw new BuildException("Either the 'file' attribute or one "
  146. + "FileSet is required.");
  147. }
  148. try {
  149. if (isFromRemote && !isToRemote) {
  150. download(fromUri, toUri);
  151. } else if (!isFromRemote && isToRemote) {
  152. if (fileSets != null) {
  153. upload(fileSets, toUri);
  154. } else {
  155. upload(fromUri, toUri);
  156. }
  157. } else if (isFromRemote && isToRemote) {
  158. // not implemented yet.
  159. } else {
  160. throw new BuildException("'todir' and 'file' attributes "
  161. + "must have syntax like the following: "
  162. + "user:password@host:/path");
  163. }
  164. } catch (Exception e) {
  165. if (getFailonerror()) {
  166. throw new BuildException(e);
  167. } else {
  168. log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
  169. }
  170. }
  171. }
  172. private void download(String fromSshUri, String toPath)
  173. throws JSchException, IOException {
  174. String file = parseUri(fromSshUri);
  175. Session session = null;
  176. try {
  177. session = openSession();
  178. ScpFromMessage message =
  179. new ScpFromMessage(getVerbose(), session, file,
  180. getProject().resolveFile(toPath),
  181. fromSshUri.endsWith("*"));
  182. log("Receiving file: " + file);
  183. message.setLogListener(this);
  184. message.execute();
  185. } finally {
  186. if (session != null) {
  187. session.disconnect();
  188. }
  189. }
  190. }
  191. private void upload(List fileSet, String toSshUri)
  192. throws IOException, JSchException {
  193. String file = parseUri(toSshUri);
  194. Session session = null;
  195. try {
  196. List list = new ArrayList(fileSet.size());
  197. for (Iterator i = fileSet.iterator(); i.hasNext();) {
  198. FileSet set = (FileSet) i.next();
  199. Directory d = createDirectory(set);
  200. if (d != null) {
  201. list.add(d);
  202. }
  203. }
  204. if (!list.isEmpty()) {
  205. session = openSession();
  206. ScpToMessage message = new ScpToMessage(getVerbose(), session,
  207. list, file);
  208. message.setLogListener(this);
  209. message.execute();
  210. }
  211. } finally {
  212. if (session != null) {
  213. session.disconnect();
  214. }
  215. }
  216. }
  217. private void upload(String fromPath, String toSshUri)
  218. throws IOException, JSchException {
  219. String file = parseUri(toSshUri);
  220. Session session = null;
  221. try {
  222. session = openSession();
  223. ScpToMessage message =
  224. new ScpToMessage(getVerbose(), session,
  225. getProject().resolveFile(fromPath), file);
  226. message.setLogListener(this);
  227. message.execute();
  228. } finally {
  229. if (session != null) {
  230. session.disconnect();
  231. }
  232. }
  233. }
  234. private String parseUri(String uri) {
  235. int indexOfAt = uri.indexOf('@');
  236. int indexOfColon = uri.indexOf(':');
  237. if (indexOfColon > -1 && indexOfColon < indexOfAt) {
  238. // user:password@host:/path notation
  239. setUsername(uri.substring(0, indexOfColon));
  240. setPassword(uri.substring(indexOfColon + 1, indexOfAt));
  241. } else {
  242. // no password, will require passphrase
  243. setUsername(uri.substring(0, indexOfAt));
  244. }
  245. if (getUserInfo().getPassword() == null
  246. && getUserInfo().getPassphrase() == null) {
  247. throw new BuildException("neither password nor passphrase for user "
  248. + getUserInfo().getName() + " has been "
  249. + "given. Can't authenticate.");
  250. }
  251. int indexOfPath = uri.indexOf(':', indexOfAt + 1);
  252. if (indexOfPath == -1) {
  253. throw new BuildException("no remote path in " + uri);
  254. }
  255. setHost(uri.substring(indexOfAt + 1, indexOfPath));
  256. String remotePath = uri.substring(indexOfPath + 1);
  257. if (remotePath.equals("")) {
  258. remotePath = ".";
  259. }
  260. return remotePath;
  261. }
  262. private boolean isRemoteUri(String uri) {
  263. boolean isRemote = true;
  264. int indexOfAt = uri.indexOf('@');
  265. if (indexOfAt < 0) {
  266. isRemote = false;
  267. }
  268. return isRemote;
  269. }
  270. private Directory createDirectory(FileSet set) {
  271. DirectoryScanner scanner = set.getDirectoryScanner(getProject());
  272. Directory root = new Directory(scanner.getBasedir());
  273. String[] files = scanner.getIncludedFiles();
  274. if (files.length != 0) {
  275. for (int j = 0; j < files.length; j++) {
  276. String[] path = Directory.getPath(files[j]);
  277. Directory current = root;
  278. File currentParent = scanner.getBasedir();
  279. for (int i = 0; i < path.length; i++) {
  280. File file = new File(currentParent, path[i]);
  281. if (file.isDirectory()) {
  282. current.addDirectory(new Directory(file));
  283. current = current.getChild(file);
  284. currentParent = current.getDirectory();
  285. } else if (file.isFile()) {
  286. current.addFile(file);
  287. }
  288. }
  289. }
  290. } else {
  291. // skip
  292. root = null;
  293. }
  294. return root;
  295. }
  296. }