package org.dru.clay.respository.transport.ssh;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.Buffer.BufferException;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.userauth.method.AuthMethod;
import net.schmizz.sshj.xfer.InMemoryDestFile;
import org.dru.clay.respository.transport.FileInfo;
import org.dru.clay.respository.transport.Transport;
import com.jcraft.jsch.agentproxy.AgentProxy;
import com.jcraft.jsch.agentproxy.Connector;
import com.jcraft.jsch.agentproxy.ConnectorFactory;
import com.jcraft.jsch.agentproxy.Identity;
import com.jcraft.jsch.agentproxy.sshj.AuthAgent;
public class SshTransport implements Transport {
private final SSHClient ssh;
public SshTransport(SshConfig config) {
this.ssh = new SSHClient();
try {
this.ssh.loadKnownHosts();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public FileInfo get(URI file) {
Session session = null;
try {
session = getSession(file);
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final InMemoryDestFile destination = new InMemoryDestFile() {
@Override
public OutputStream getOutputStream() throws IOException {
return outputStream;
}
};
ssh.newSCPFileTransfer().download(file.getPath(), destination);
final byte[] content = outputStream.toByteArray();
final long length = content.length;
final long lastModified = System.currentTimeMillis(); // TODO: not good
return new FileInfo(length, lastModified, content);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
close(session);
}
}
@Override
public void get(URI file, File destination) {
Session session = null;
try {
session = getSession(file);
ssh.newSCPFileTransfer().download(file.getPath(), destination.getAbsolutePath());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
close(session);
}
}
@Override
public void getIfNewer(URI file, File destination) {
// TODO: implements this if it's possible
get(file, destination);
}
@Override
public void put(File source, URI destination) {
Session session = null;
try {
session = getSession(destination);
ssh.newSCPFileTransfer().upload(source.getAbsolutePath(), destination.getPath());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
close(session);
}
}
@Override
public List<URI> list(URI directory) {
Session session = null;
try {
session = getSession(directory);
final List<URI> uris = new ArrayList<URI>();
final Command command = session.exec("ls -p1 --group-directories-first /" + directory.getPath());
final String result = IOUtils.readFully(command.getInputStream()).toString();
for (String line : result.split("[\n\r]+")) {
if (line.endsWith("/")) {
final String dir = line.substring(0, line.length() - 2);
uris.add(new URI(directory + "/" + dir));
}
}
return uris;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
close(session);
}
}
private void close(Session session) {
if (session != null && session.isOpen()) {
try {
session.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void cleanup() {
if (ssh.isConnected()) {
try {
ssh.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private synchronized Session getSession(URI directory) {
try {
if (!ssh.isConnected()) {
final int port = directory.getPort() < 0 ? 22 : directory.getPort();
final String hostname = directory.getHost();
ssh.connect(hostname, port);
}
if (!ssh.isAuthenticated()) {
final Connector connector = ConnectorFactory.getDefault().createConnector();
final String username = (directory.getUserInfo() == null) ? System.getProperty("user.name") : directory.getUserInfo();
if (connector == null) {
ssh.authPublickey(username);
// TODO: find other ways to authenticate the user
throw new UnsupportedOperationException("No SSH agent found");
} else {
final AgentProxy proxy = new AgentProxy(connector);
ssh.auth(username, getAuthMethods(proxy));
}
}
// TODO: Check if the connection is against the correct server
// if (ssh.getRemoteHostname())
return ssh.startSession();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static List<AuthMethod> getAuthMethods(AgentProxy agent) throws BufferException {
Identity[] identities = agent.getIdentities();
List<AuthMethod> result = new ArrayList<AuthMethod>();
for (Identity identity : identities) {
result.add(new AuthAgent(agent, identity));
}
return result;
}
}