/*******************************************************************************
* Copyright 2017 Capital One Services, LLC and Bitwise, Inc.
* 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 hydrograph.ui.communication.utilities;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import hydrograph.ui.communication.messages.Message;
import hydrograph.ui.communication.messages.MessageType;
import hydrograph.ui.logging.factory.LogFactory;
/**
* The class SCPUtility
* Provides utility methods to copy files using scp
*
* @author Bitwise
*
*/
public class SCPUtility {
private static final String STRICT_HOST_KEY_CHECKING_DEFAULT_VALUE = "no";
private static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
private static final String PREFERRED_AUTHENTICATIONS_DEFAULT_VALUES = "publickey,keyboard-interactive,password";
private static final String PREFERRED_AUTHENTICATIONS = "PreferredAuthentications";
private static final int SSH_PORT = 22;
private static final String GENERAL_SUCCESS_MESSAGE = "SUCCESS";
private static final String GENERAL_ERROR_MESSAGE = "ERROR";
private static final String AUTHENTICATION_FAILED_MESSAGE = "Invalid username or password";
private static final String AUTH_FAIL_EXCEPTION = "Auth fail";
private static final String UNKNOWN_HOST_MESSAGE = "Unknown host";
private static final String UNKNOWN_HOST_EXCEPTION = "UnknownHostException";
private static final Logger logger = LogFactory.INSTANCE.getLogger(SCPUtility.class);
public static SCPUtility INSTANCE = new SCPUtility();
private SCPUtility(){
}
public Message validateCredentials(String host, String user, String password) {
JSch jsch = new JSch();
Session session;
try {
session = jsch.getSession(user, host, SSH_PORT);
session.setPassword(password);
java.util.Properties config = new java.util.Properties();
config.put(STRICT_HOST_KEY_CHECKING, STRICT_HOST_KEY_CHECKING_DEFAULT_VALUE);
session.setConfig(config);
session.setConfig(PREFERRED_AUTHENTICATIONS, PREFERRED_AUTHENTICATIONS_DEFAULT_VALUES);
session.connect();
session.disconnect();
} catch (Exception e) {
return getErrorMessage(e);
}
return new Message(MessageType.SUCCESS, GENERAL_SUCCESS_MESSAGE);
}
private Message getErrorMessage(Exception e) {
if(StringUtils.contains(e.getMessage(), UNKNOWN_HOST_EXCEPTION)){
return new Message(MessageType.UNKNOWN_HOST, UNKNOWN_HOST_MESSAGE);
}else if(StringUtils.contains(e.getMessage(), AUTH_FAIL_EXCEPTION)){
return new Message(MessageType.INVALID_USERNAME_PASSWORD, AUTHENTICATION_FAILED_MESSAGE);
}else{
return new Message(MessageType.ERROR, GENERAL_ERROR_MESSAGE);
}
}
/**
*
* Scp file from remote server
*
* @param host
* @param user
* @param password
* @param remoteFile
* @param localFile
* @throws JSchException
* @throws IOException
*/
public void scpFileFromRemoteServer(String host, String user, String password, String remoteFile, String localFile) throws JSchException, IOException {
String prefix = null;
if (new File(localFile).isDirectory()) {
prefix = localFile + File.separator;
}
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
// username and password will be given via UserInfo interface.
UserInfo userInfo = new UserInformation(password);
session.setUserInfo(userInfo);
session.connect();
// exec 'scp -f remoteFile' remotely
String command = "scp -f " + remoteFile;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
// get I/O streams for remote scp
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
channel.connect();
byte[] buf = new byte[1024];
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
readRemoteFileAndWriteToLocalFile(localFile, prefix, out, in, buf);
session.disconnect();
}
private void readRemoteFileAndWriteToLocalFile(String localFile, String prefix,
OutputStream out, InputStream in, byte[] buf) throws IOException, FileNotFoundException {
while (true) {
int c = checkAck(in);
if (c != 'C') {
break;
}
// read '0644 '
in.read(buf, 0, 5);
long filesize = 0L;
while (true) {
if (in.read(buf, 0, 1) < 0) {
// error
break;
}
if (buf[0] == ' ')
break;
filesize = filesize * 10L + (long) (buf[0] - '0');
}
String file = null;
for (int i = 0;; i++) {
in.read(buf, i, 1);
if (buf[i] == (byte) 0x0a) {
file = new String(buf, 0, i);
break;
}
}
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
// read a content of local file
try (FileOutputStream fos = new FileOutputStream(prefix == null ? localFile : prefix + file)){
int foo;
while (true) {
if (buf.length < filesize)
foo = buf.length;
else
foo = (int) filesize;
foo = in.read(buf, 0, foo);
if (foo < 0) {
// error
break;
}
fos.write(buf, 0, foo);
filesize -= foo;
if (filesize == 0L)
break;
}
} catch (IOException e) {
throw e;
}
if (checkAck(in) != 0) {
//System.exit(0);
return;
}
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
}
}
private int checkAck(InputStream in) throws IOException {
int b = in.read();
// b may be 0 for success,
// 1 for error,
// 2 for fatal error,
// -1
if (b == 0)
return b;
if (b == -1)
return b;
if (b == 1 || b == 2) {
StringBuffer sb = new StringBuffer();
int c;
do {
c = in.read();
sb.append((char) c);
} while (c != '\n');
if (b == 1) { // error
logger.debug(sb.toString());
}
if (b == 2) { // fatal error
logger.debug(sb.toString());
}
}
return b;
}
/**
*
* This class store user information who participates in scp
*
* @author Bitwise
*
*/
private static class UserInformation implements UserInfo {
String password;
public UserInformation(String password) {
this.password = password;
}
@Override
public String getPassphrase() {
return password;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public boolean promptPassphrase(String arg0) {
return true;
}
@Override
public boolean promptPassword(String arg0) {
return true;
}
@Override
public boolean promptYesNo(String arg0) {
return true;
}
@Override
public void showMessage(String arg0) {
}
}
}