blob: e1a1bdb779d4c66b381f007270fc744f222eabbe [file] [log] [blame]
package ch.ethz.ssh2;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
/**
* A very basic <code>SCPClient</code> that can be used to copy files from/to
* the SSH-2 server. On the server side, the "scp" program must be in the PATH.
* <p/>
* This scp client is thread safe - you can download (and upload) different sets
* of files concurrently without any troubles. The <code>SCPClient</code> is
* actually mapping every request to a distinct {@link ch.ethz.ssh2.Session}.
*
* @author Christian Plattner, plattner@inf.ethz.ch
* @version $Id: SCPClient.java 32 2011-05-28 21:56:21Z dkocher@sudo.ch $
*/
public class SCPClient
{
Connection conn;
String charsetName = null;
/**
* Set the charset used to convert between Java Unicode Strings and byte encodings
* used by the server for paths and file names.
*
* @param charset the name of the charset to be used or <code>null</code> to use the platform's
* default encoding.
* @throws IOException
* @see #getCharset()
*/
public void setCharset(String charset) throws IOException
{
if (charset == null)
{
charsetName = charset;
return;
}
try
{
Charset.forName(charset);
}
catch (UnsupportedCharsetException e)
{
throw (IOException) new IOException("This charset is not supported").initCause(e);
}
charsetName = charset;
}
/**
* The currently used charset for filename encoding/decoding.
*
* @return The name of the charset (<code>null</code> if the platform's default charset is being used)
* @see #setCharset(String)
*/
public String getCharset()
{
return charsetName;
}
public class LenNamePair
{
public long length;
String filename;
}
public SCPClient(Connection conn)
{
if (conn == null)
throw new IllegalArgumentException("Cannot accept null argument!");
this.conn = conn;
}
protected void readResponse(InputStream is) throws IOException
{
int c = is.read();
if (c == 0)
return;
if (c == -1)
throw new IOException("Remote scp terminated unexpectedly.");
if ((c != 1) && (c != 2))
throw new IOException("Remote scp sent illegal error code.");
if (c == 2)
throw new IOException("Remote scp terminated with error.");
String err = receiveLine(is);
throw new IOException("Remote scp terminated with error (" + err + ").");
}
protected String receiveLine(InputStream is) throws IOException
{
StringBuilder sb = new StringBuilder(30);
while (true)
{
/* This is a random limit - if your path names are longer, then adjust it */
if (sb.length() > 8192)
throw new IOException("Remote scp sent a too long line");
int c = is.read();
if (c < 0)
throw new IOException("Remote scp terminated unexpectedly.");
if (c == '\n')
break;
sb.append((char) c);
}
return sb.toString();
}
protected LenNamePair parseCLine(String line) throws IOException
{
/* Minimum line: "xxxx y z" ---> 8 chars */
if (line.length() < 8)
throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
throw new IOException("Malformed C line sent by remote SCP binary.");
int length_name_sep = line.indexOf(' ', 5);
if (length_name_sep == -1)
throw new IOException("Malformed C line sent by remote SCP binary.");
String length_substring = line.substring(5, length_name_sep);
String name_substring = line.substring(length_name_sep + 1);
if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
throw new IOException("Malformed C line sent by remote SCP binary.");
if ((6 + length_substring.length() + name_substring.length()) != line.length())
throw new IOException("Malformed C line sent by remote SCP binary.");
final long len;
try
{
len = Long.parseLong(length_substring);
}
catch (NumberFormatException e)
{
throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
}
if (len < 0)
throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
LenNamePair lnp = new LenNamePair();
lnp.length = len;
lnp.filename = name_substring;
return lnp;
}
/**
* The session for opened for this SCP transfer must be closed using
* SCPOutputStream#close
*
* @param remoteFile
* @param length The size of the file to send
* @param remoteTargetDirectory
* @param mode
* @return
* @throws IOException
*/
public SCPOutputStream put(final String remoteFile, long length, String remoteTargetDirectory, String mode)
throws IOException
{
Session sess = null;
if (null == remoteFile)
throw new IllegalArgumentException("Null argument.");
if (null == remoteTargetDirectory)
remoteTargetDirectory = "";
if (null == mode)
mode = "0600";
if (mode.length() != 4)
throw new IllegalArgumentException("Invalid mode.");
for (int i = 0; i < mode.length(); i++)
if (Character.isDigit(mode.charAt(i)) == false)
throw new IllegalArgumentException("Invalid mode.");
remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
String cmd = "scp -t -d \"" + remoteTargetDirectory + "\"";
sess = conn.openSession();
sess.execCommand(cmd, charsetName);
return new SCPOutputStream(this, sess, remoteFile, length, mode);
}
/**
* The session for opened for this SCP transfer must be closed using
* SCPInputStream#close
*
* @param remoteFile
* @return
* @throws IOException
*/
public SCPInputStream get(final String remoteFile) throws IOException
{
Session sess = null;
if (null == remoteFile)
throw new IllegalArgumentException("Null argument.");
if (remoteFile.length() == 0)
throw new IllegalArgumentException("Cannot accept empty filename.");
String cmd = "scp -f";
cmd += (" \"" + remoteFile + "\"");
sess = conn.openSession();
sess.execCommand(cmd, charsetName);
return new SCPInputStream(this, sess);
}
}