/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.utils.ssh.jsch; import java.io.File; import java.io.IOException; import org.apache.commons.logging.LogFactory; import org.apache.tools.ant.taskdefs.optional.ssh.ScpFromMessage; import org.apache.tools.ant.taskdefs.optional.ssh.ScpToMessage; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; /** * Provides SCP and remote-to-remote copy operations for established JSch sessions. * * @author Robert Mischke * @author Doreen Seider */ public final class JschFileTransfer { private static final String SLASH = "/"; /** * Arbitrary "short wait" for remote-to-remote copy operations. */ private static final int SHORT_WAIT_MSEC = 100; private JschFileTransfer() { // prevent instantiation } /** * Uploads a file with SCP. * * @param session an established JSch session * @param localFile the local {@link File} to upload * @param remotePath the remote target path of the file to copy, using a relative path to the initial SSH "home" directory if necessary * @throws JSchException on general SSH errors * @throws IOException on SCP operation failure */ public static void uploadFile(Session session, File localFile, String remotePath) throws IOException, JSchException { ScpToMessage message = new ScpToMessage(session, localFile, remotePath); message.execute(); } /** * Uploads directories with SCP. * * @param session an established JSch session * @param directory the local directory to upload * @param remotePath the remote target path of the directory to copy, using a relative path to the * initial SSH "home" directory if necessary * @throws JSchException on general SSH errors * @throws IOException on SCP operation failure * @throws InterruptedException if creating directory failed */ public static void uploadDirectory(Session session, File directory, String remotePath) throws IOException, JSchException, InterruptedException { remotePath = remotePath + "/" + directory.getName(); ChannelExec channel = (ChannelExec) session.openChannel("exec"); // NOTE: the provided paths are expected to require no escaping channel.setCommand("mkdir -p " + remotePath); channel.connect(); while (!channel.isClosed()) { // dir creation is usually fast, so only wait for a short time Thread.sleep(SHORT_WAIT_MSEC); } channel.disconnect(); if (channel.getExitStatus() != 0) { throw new IOException("Creating directory failed: " + remotePath); } for (File file : directory.listFiles()) { if (file.isDirectory()) { uploadDirectory(session, file, remotePath); } else { uploadFile(session, file, remotePath); } } } /** * Uploads directories with SCP. Expects that parent directories (if not existing) will be created by the server. * * @param session an established JSch session * @param directory the local directory to upload * @param remotePath the remote target path of the directory to copy, using a relative path to the initial SSH "home" directory if * necessary * @throws JSchException on general SSH errors * @throws IOException on SCP operation failure * @throws InterruptedException if creating directory failed */ public static void uploadDirectoryToRCEInstance(Session session, File directory, String remotePath) throws IOException, JSchException, InterruptedException { for (File file : directory.listFiles()) { if (file.isDirectory()) { uploadDirectoryToRCEInstance(session, file, remotePath + SLASH + file.getName()); } else { uploadFile(session, file, remotePath + SLASH + file.getName()); } } } /** * Downloads a file with SCP. * * @param session an established JSch session * @param localFile the local {@link File} to download to * @param remotePath the remote source path of the file to copy, using a relative path to the initial SSH "home" directory if necessary * @throws JSchException on general SSH errors * @throws IOException on SCP operation failure */ public static void downloadFile(Session session, String remotePath, File localFile) throws IOException, JSchException { // "false" = not recursive ScpFromMessage message = new ScpFromMessage(session, remotePath, localFile, false); message.execute(); } /** * Downloads a directory with SCP. * * @param session an established JSch session * @param localDir the local directory ({@link File}) to download to * @param remotePath the remote source path of the file to copy, using a relative path to the initial SSH "home" directory if necessary * @throws JSchException on general SSH errors * @throws IOException on SCP operation failure */ public static void downloadDirectory(Session session, String remotePath, File localDir) throws IOException, JSchException { // "true" = recursive ScpFromMessage message = new ScpFromMessage(session, remotePath, localDir, true); message.execute(); } /** * Performs a file copy operation on the remove host through an existing SSH connection. * * IMPORTANT: This method uses the standard "mkdir" and "cp" shell commands on the remote system, and will therefore not work on * standard Windows hosts. * * @param jschSession an established JSch session * @param source the source path of the file to copy, using a relative path to the initial SSH "home" directory if necessary. IMPORTANT: * this path is expected to work without shell escaping; in particular, it should not contain spaces or reserved shell * characters. * @param target the target path of the file to copy, using a relative path to the initial SSH "home" directory if necessary; if the * containing directory does not exist, it will be created. IMPORTANT: this path is expected to work without shell escaping; in * particular, it should not contain spaces or reserved shell characters. * @throws JSchException on general SSH exceptions * @throws IOException if the remote copy operation returned a non-zero exit code * @throws InterruptedException if the thread is interrupted while waiting for the copy to complete */ public static void remoteToRemoteCopy(Session jschSession, String source, String target) throws JSchException, IOException, InterruptedException { // generate a "mkdir" command for the target directory, if present String mkdirCommandPrefix = ""; int separatorPos = target.lastIndexOf(SLASH); if (separatorPos >= 0) { // contains slash -> has directory part String directoryPart = target.substring(0, separatorPos); mkdirCommandPrefix = "mkdir -p " + directoryPart + " && "; } ChannelExec channel = (ChannelExec) jschSession.openChannel("exec"); // NOTE: the provided paths are expected to require no escaping String fullCommand = mkdirCommandPrefix + "cp " + source + " " + target; LogFactory.getLog(JschFileTransfer.class).debug("Performing remote copy: " + fullCommand); channel.setCommand(fullCommand); channel.connect(); while (!channel.isClosed()) { // file copy is usually fast, so only wait for a short time Thread.sleep(SHORT_WAIT_MSEC); } channel.disconnect(); if (channel.getExitStatus() != 0) { throw new IOException("Remote copy operation failed: " + source + " -> " + target); } } }