/** * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package datameer.awstasks.ssh; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import awstasks.com.jcraft.jsch.Channel; import awstasks.com.jcraft.jsch.Session; import datameer.awstasks.util.IoUtil; import datameer.awstasks.util.SshUtil; public class ScpDownloadCommand extends JschCommand { private static final byte LINE_FEED = 0x0a; private static final String SCP_DOWNLOAD_COMMAND = "scp -f "; private final String _remoteFile; private final File _localFile; private final boolean _recursive; public ScpDownloadCommand(String remoteFile, File localFile, boolean recursive) { _remoteFile = remoteFile; _localFile = localFile; _recursive = recursive; } @Override public void execute(Session session) throws IOException { String command = constructScpInitCommand(_remoteFile, _recursive); Channel channel = SshUtil.openExecChannel(session, command); try { OutputStream out = channel.getOutputStream(); InputStream in = channel.getInputStream(); SshUtil.sendAckOk(out); download(in, out, _localFile); } finally { if (channel != null) { channel.disconnect(); } } } protected final static String constructScpInitCommand(String remoteFile, boolean recursive) { String command = SCP_DOWNLOAD_COMMAND; if (recursive) { command += "-r "; } command += remoteFile.replace(" ", "\\ "); return command; } private final static void download(InputStream in, OutputStream out, File localFile) throws IOException { File startFile = localFile; while (true) { // C0644 filesize filename - header for a regular file // T time 0 time 0\n - present if perserve time. // D directory - this is the header for a directory. String serverResponse = readServerResponse(in); if (serverResponse == null) { return; } if (serverResponse.charAt(0) == 'C') { parseAndDownloadFile(serverResponse, startFile, out, in); } else if (serverResponse.charAt(0) == 'D') { startFile = parseAndCreateDirectory(serverResponse, startFile); SshUtil.sendAckOk(out); } else if (serverResponse.charAt(0) == 'E') { startFile = startFile.getParentFile(); SshUtil.sendAckOk(out); } else if (serverResponse.charAt(0) == '\01' || serverResponse.charAt(0) == '\02') { // this indicates an error. throw new IOException(serverResponse.substring(1)); } } } protected static String readServerResponse(InputStream in) throws IOException, UnsupportedEncodingException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); while (true) { int read = in.read(); if (read < 0) { return null; } if ((byte) read == LINE_FEED) { break; } stream.write(read); } String serverResponse = stream.toString("UTF-8"); return serverResponse; } private final static File parseAndCreateDirectory(String serverResponse, File localFile) { int start = serverResponse.indexOf(" "); // appears that the next token is not used and it's zero. start = serverResponse.indexOf(" ", start + 1); String directoryName = serverResponse.substring(start + 1); if (localFile.isDirectory()) { File dir = new File(localFile, directoryName); dir.mkdir(); LOG.info("Creating: " + dir); return dir; } return null; } private final static void parseAndDownloadFile(String serverResponse, File localFile, OutputStream out, InputStream in) throws IOException { int start = 0; int end = serverResponse.indexOf(" ", start + 1); start = end + 1; end = serverResponse.indexOf(" ", start + 1); long filesize = Long.parseLong(serverResponse.substring(start, end)); String filename = serverResponse.substring(end + 1); LOG.info("Receiving: " + filename + " : " + filesize); File transferFile = (localFile.isDirectory()) ? new File(localFile, filename) : localFile; downloadFile(transferFile, filesize, out, in); SshUtil.checkAcknowledgement(in); SshUtil.sendAckOk(out); } private final static void downloadFile(File localFile, long filesize, OutputStream out, InputStream in) throws IOException { SshUtil.sendAckOk(out); // read a content of lfile FileOutputStream fos = new FileOutputStream(localFile); long totalLength = 0; long startTime = System.currentTimeMillis(); try { IoUtil.copyBytes(in, fos, filesize); } finally { long endTime = System.currentTimeMillis(); logStats(startTime, endTime, totalLength); fos.flush(); fos.close(); } } }