• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package ch.ethz.ssh2;
2 
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.nio.charset.Charset;
6 import java.nio.charset.UnsupportedCharsetException;
7 
8 /**
9  * A very basic <code>SCPClient</code> that can be used to copy files from/to
10  * the SSH-2 server. On the server side, the "scp" program must be in the PATH.
11  * <p/>
12  * This scp client is thread safe - you can download (and upload) different sets
13  * of files concurrently without any troubles. The <code>SCPClient</code> is
14  * actually mapping every request to a distinct {@link ch.ethz.ssh2.Session}.
15  *
16  * @author Christian Plattner, plattner@inf.ethz.ch
17  * @version $Id: SCPClient.java 32 2011-05-28 21:56:21Z dkocher@sudo.ch $
18  */
19 
20 public class SCPClient
21 {
22 	Connection conn;
23 
24 	String charsetName = null;
25 
26 	/**
27 	 * Set the charset used to convert between Java Unicode Strings and byte encodings
28 	 * used by the server for paths and file names.
29 	 *
30 	 * @param charset the name of the charset to be used or <code>null</code> to use the platform's
31 	 * default encoding.
32 	 * @throws IOException
33 	 * @see #getCharset()
34 	 */
setCharset(String charset)35 	public void setCharset(String charset) throws IOException
36 	{
37 		if (charset == null)
38 		{
39 			charsetName = charset;
40 			return;
41 		}
42 
43 		try
44 		{
45 			Charset.forName(charset);
46 		}
47 		catch (UnsupportedCharsetException e)
48 		{
49 			throw (IOException) new IOException("This charset is not supported").initCause(e);
50 		}
51 		charsetName = charset;
52 	}
53 
54 	/**
55 	 * The currently used charset for filename encoding/decoding.
56 	 *
57 	 * @return The name of the charset (<code>null</code> if the platform's default charset is being used)
58 	 * @see #setCharset(String)
59 	 */
getCharset()60 	public String getCharset()
61 	{
62 		return charsetName;
63 	}
64 
65 	public class LenNamePair
66 	{
67 		public long length;
68 		String filename;
69 	}
70 
SCPClient(Connection conn)71 	public SCPClient(Connection conn)
72 	{
73 		if (conn == null)
74 			throw new IllegalArgumentException("Cannot accept null argument!");
75 		this.conn = conn;
76 	}
77 
readResponse(InputStream is)78 	protected void readResponse(InputStream is) throws IOException
79 	{
80 		int c = is.read();
81 
82 		if (c == 0)
83 			return;
84 
85 		if (c == -1)
86 			throw new IOException("Remote scp terminated unexpectedly.");
87 
88 		if ((c != 1) && (c != 2))
89 			throw new IOException("Remote scp sent illegal error code.");
90 
91 		if (c == 2)
92 			throw new IOException("Remote scp terminated with error.");
93 
94 		String err = receiveLine(is);
95 		throw new IOException("Remote scp terminated with error (" + err + ").");
96 	}
97 
receiveLine(InputStream is)98 	protected String receiveLine(InputStream is) throws IOException
99 	{
100 		StringBuilder sb = new StringBuilder(30);
101 
102 		while (true)
103 		{
104 			/* This is a random limit - if your path names are longer, then adjust it */
105 
106 			if (sb.length() > 8192)
107 				throw new IOException("Remote scp sent a too long line");
108 
109 			int c = is.read();
110 
111 			if (c < 0)
112 				throw new IOException("Remote scp terminated unexpectedly.");
113 
114 			if (c == '\n')
115 				break;
116 
117 			sb.append((char) c);
118 
119 		}
120 		return sb.toString();
121 	}
122 
parseCLine(String line)123 	protected LenNamePair parseCLine(String line) throws IOException
124 	{
125 		/* Minimum line: "xxxx y z" ---> 8 chars */
126 
127 		if (line.length() < 8)
128 			throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
129 
130 		if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
131 			throw new IOException("Malformed C line sent by remote SCP binary.");
132 
133 		int length_name_sep = line.indexOf(' ', 5);
134 
135 		if (length_name_sep == -1)
136 			throw new IOException("Malformed C line sent by remote SCP binary.");
137 
138 		String length_substring = line.substring(5, length_name_sep);
139 		String name_substring = line.substring(length_name_sep + 1);
140 
141 		if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
142 			throw new IOException("Malformed C line sent by remote SCP binary.");
143 
144 		if ((6 + length_substring.length() + name_substring.length()) != line.length())
145 			throw new IOException("Malformed C line sent by remote SCP binary.");
146 
147 		final long len;
148 		try
149 		{
150 			len = Long.parseLong(length_substring);
151 		}
152 		catch (NumberFormatException e)
153 		{
154 			throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
155 		}
156 
157 		if (len < 0)
158 			throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
159 
160 		LenNamePair lnp = new LenNamePair();
161 		lnp.length = len;
162 		lnp.filename = name_substring;
163 
164 		return lnp;
165 	}
166 
167 	/**
168 	 * The session for opened for this SCP transfer must be closed using
169 	 * SCPOutputStream#close
170 	 *
171 	 * @param remoteFile
172 	 * @param length The size of the file to send
173 	 * @param remoteTargetDirectory
174 	 * @param mode
175 	 * @return
176 	 * @throws IOException
177 	 */
put(final String remoteFile, long length, String remoteTargetDirectory, String mode)178 	public SCPOutputStream put(final String remoteFile, long length, String remoteTargetDirectory, String mode)
179 			throws IOException
180 	{
181 		Session sess = null;
182 
183 		if (null == remoteFile)
184 			throw new IllegalArgumentException("Null argument.");
185 		if (null == remoteTargetDirectory)
186 			remoteTargetDirectory = "";
187 		if (null == mode)
188 			mode = "0600";
189 		if (mode.length() != 4)
190 			throw new IllegalArgumentException("Invalid mode.");
191 
192 		for (int i = 0; i < mode.length(); i++)
193 			if (Character.isDigit(mode.charAt(i)) == false)
194 				throw new IllegalArgumentException("Invalid mode.");
195 
196 		remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
197 
198 		String cmd = "scp -t -d \"" + remoteTargetDirectory + "\"";
199 
200 		sess = conn.openSession();
201 		sess.execCommand(cmd, charsetName);
202 
203 		return new SCPOutputStream(this, sess, remoteFile, length, mode);
204 	}
205 
206 	/**
207 	 * The session for opened for this SCP transfer must be closed using
208 	 * SCPInputStream#close
209 	 *
210 	 * @param remoteFile
211 	 * @return
212 	 * @throws IOException
213 	 */
get(final String remoteFile)214 	public SCPInputStream get(final String remoteFile) throws IOException
215 	{
216 		Session sess = null;
217 
218 		if (null == remoteFile)
219 			throw new IllegalArgumentException("Null argument.");
220 
221 		if (remoteFile.length() == 0)
222 			throw new IllegalArgumentException("Cannot accept empty filename.");
223 
224 		String cmd = "scp -f";
225 		cmd += (" \"" + remoteFile + "\"");
226 
227 		sess = conn.openSession();
228 		sess.execCommand(cmd, charsetName);
229 
230 		return new SCPInputStream(this, sess);
231 	}
232 }