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 java.util.StringTokenizer;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.io.EOFException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.io.FileOutputStream;
  25. import java.io.ByteArrayOutputStream;
  26. import com.jcraft.jsch.JSchException;
  27. import com.jcraft.jsch.Session;
  28. import com.jcraft.jsch.Channel;
  29. public class ScpFromMessage extends AbstractSshMessage {
  30. private final byte LINE_FEED = 0x0a;
  31. private final int BUFFER_SIZE = 1024;
  32. private String remoteFile;
  33. private File localFile;
  34. private boolean isRecursive = false;
  35. /**
  36. * @since Ant 1.6.2
  37. */
  38. public ScpFromMessage(boolean verbose,
  39. Session session,
  40. String aRemoteFile,
  41. File aLocalFile,
  42. boolean recursive) {
  43. super(verbose, session);
  44. this.remoteFile = aRemoteFile;
  45. this.localFile = aLocalFile;
  46. this.isRecursive = recursive;
  47. }
  48. public ScpFromMessage(Session session,
  49. String aRemoteFile,
  50. File aLocalFile,
  51. boolean recursive) {
  52. this(false, session, aRemoteFile, aLocalFile, recursive);
  53. }
  54. public void execute() throws IOException, JSchException {
  55. String command = "scp -f ";
  56. if (isRecursive) {
  57. command += "-r ";
  58. }
  59. command += remoteFile;
  60. Channel channel = openExecChannel(command);
  61. try {
  62. // get I/O streams for remote scp
  63. OutputStream out = channel.getOutputStream();
  64. InputStream in = channel.getInputStream();
  65. channel.connect();
  66. sendAck(out);
  67. startRemoteCpProtocol(in, out, localFile);
  68. } finally {
  69. if (channel != null) {
  70. channel.disconnect();
  71. }
  72. }
  73. log("done\n");
  74. }
  75. private void startRemoteCpProtocol(InputStream in,
  76. OutputStream out,
  77. File localFile) throws IOException {
  78. File startFile = localFile;
  79. while (true) {
  80. // C0644 filesize filename - header for a regular file
  81. // T time 0 time 0\n - present if perserve time.
  82. // D directory - this is the header for a directory.
  83. ByteArrayOutputStream stream = new ByteArrayOutputStream();
  84. while (true) {
  85. int read = in.read();
  86. if (read < 0) {
  87. return;
  88. }
  89. if ((byte) read == LINE_FEED) {
  90. break;
  91. }
  92. stream.write(read);
  93. }
  94. String serverResponse = stream.toString("UTF-8");
  95. if (serverResponse.charAt(0) == 'C') {
  96. parseAndFetchFile(serverResponse, startFile, out, in);
  97. } else if (serverResponse.charAt(0) == 'D') {
  98. startFile = parseAndCreateDirectory(serverResponse,
  99. startFile);
  100. sendAck(out);
  101. } else if (serverResponse.charAt(0) == 'E') {
  102. startFile = startFile.getParentFile();
  103. sendAck(out);
  104. } else if (serverResponse.charAt(0) == '\01'
  105. || serverResponse.charAt(0) == '\02') {
  106. // this indicates an error.
  107. throw new IOException(serverResponse.substring(1));
  108. }
  109. }
  110. }
  111. private File parseAndCreateDirectory(String serverResponse,
  112. File localFile) {
  113. int start = serverResponse.indexOf(" ");
  114. // appears that the next token is not used and it's zero.
  115. start = serverResponse.indexOf(" ", start + 1);
  116. String directoryName = serverResponse.substring(start + 1);
  117. if (localFile.isDirectory()) {
  118. File dir = new File(localFile, directoryName);
  119. dir.mkdir();
  120. log("Creating: " + dir);
  121. return dir;
  122. }
  123. return null;
  124. }
  125. private void parseAndFetchFile(String serverResponse,
  126. File localFile,
  127. OutputStream out,
  128. InputStream in) throws IOException {
  129. int start = 0;
  130. int end = serverResponse.indexOf(" ", start + 1);
  131. String command = serverResponse.substring(start, end);
  132. start = end + 1;
  133. end = serverResponse.indexOf(" ", start + 1);
  134. int filesize = Integer.parseInt(serverResponse.substring(start, end));
  135. String filename = serverResponse.substring(end + 1);
  136. log("Receiving: " + filename + " : " + filesize);
  137. File transferFile = (localFile.isDirectory())
  138. ? new File(localFile, filename)
  139. : localFile;
  140. fetchFile(transferFile, filesize, out, in);
  141. waitForAck(in);
  142. sendAck(out);
  143. }
  144. private void fetchFile(File localFile,
  145. int filesize,
  146. OutputStream out,
  147. InputStream in) throws IOException {
  148. byte[] buf = new byte[BUFFER_SIZE];
  149. sendAck(out);
  150. // read a content of lfile
  151. FileOutputStream fos = new FileOutputStream(localFile);
  152. int length;
  153. int totalLength = 0;
  154. long startTime = System.currentTimeMillis();
  155. // only track progress for files larger than 100kb in verbose mode
  156. boolean trackProgress = getVerbose() && filesize > 102400;
  157. // since filesize keeps on decreasing we have to store the
  158. // initial filesize
  159. int initFilesize = filesize;
  160. int percentTransmitted = 0;
  161. try {
  162. while (true) {
  163. length = in.read(buf, 0,
  164. (buf.length < filesize) ? buf.length : filesize);
  165. if (length < 0) {
  166. throw new EOFException("Unexpected end of stream.");
  167. }
  168. fos.write(buf, 0, length);
  169. filesize -= length;
  170. totalLength += length;
  171. if (filesize == 0) {
  172. break;
  173. }
  174. if (trackProgress) {
  175. percentTransmitted = trackProgress(initFilesize,
  176. totalLength,
  177. percentTransmitted);
  178. }
  179. }
  180. } finally {
  181. long endTime = System.currentTimeMillis();
  182. logStats(startTime, endTime, totalLength);
  183. fos.flush();
  184. fos.close();
  185. }
  186. }
  187. }