1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 package org.eclipse.jgit.transport;
50
51 import java.io.BufferedOutputStream;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.OutputStream;
55 import java.util.ArrayList;
56 import java.util.Collection;
57 import java.util.List;
58 import java.util.concurrent.Callable;
59 import java.util.concurrent.TimeUnit;
60
61 import org.eclipse.jgit.errors.TransportException;
62 import org.eclipse.jgit.internal.JGitText;
63 import org.eclipse.jgit.util.io.IsolatedOutputStream;
64
65 import com.jcraft.jsch.Channel;
66 import com.jcraft.jsch.ChannelExec;
67 import com.jcraft.jsch.ChannelSftp;
68 import com.jcraft.jsch.JSchException;
69 import com.jcraft.jsch.Session;
70 import com.jcraft.jsch.SftpException;
71
72
73
74
75
76
77
78
79 public class JschSession implements RemoteSession {
80 final Session sock;
81 final URIish uri;
82
83
84
85
86
87
88
89
90
91
92 public JschSession(Session session, URIish uri) {
93 sock = session;
94 this.uri = uri;
95 }
96
97
98 @Override
99 public Process exec(String command, int timeout) throws IOException {
100 return new JschProcess(command, timeout);
101 }
102
103
104 @Override
105 public void disconnect() {
106 if (sock.isConnected())
107 sock.disconnect();
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121 @Deprecated
122 public Channel getSftpChannel() throws JSchException {
123 return sock.openChannel("sftp");
124 }
125
126
127
128
129
130
131 @Override
132 public FtpChannel getFtpChannel() {
133 return new JschFtpChannel();
134 }
135
136
137
138
139
140
141
142 private class JschProcess extends Process {
143 private ChannelExec channel;
144
145 final int timeout;
146
147 private InputStream inputStream;
148
149 private OutputStream outputStream;
150
151 private InputStream errStream;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 JschProcess(String commandName, int tms)
168 throws TransportException, IOException {
169 timeout = tms;
170 try {
171 channel = (ChannelExec) sock.openChannel("exec");
172 channel.setCommand(commandName);
173 setupStreams();
174 channel.connect(timeout > 0 ? timeout * 1000 : 0);
175 if (!channel.isConnected()) {
176 closeOutputStream();
177 throw new TransportException(uri,
178 JGitText.get().connectionFailed);
179 }
180 } catch (JSchException e) {
181 closeOutputStream();
182 throw new TransportException(uri, e.getMessage(), e);
183 }
184 }
185
186 private void closeOutputStream() {
187 if (outputStream != null) {
188 try {
189 outputStream.close();
190 } catch (IOException ioe) {
191
192 }
193 }
194 }
195
196 private void setupStreams() throws IOException {
197 inputStream = channel.getInputStream();
198
199
200
201
202
203
204 OutputStream out = channel.getOutputStream();
205 if (timeout <= 0) {
206 outputStream = out;
207 } else {
208 IsolatedOutputStream i = new IsolatedOutputStream(out);
209 outputStream = new BufferedOutputStream(i, 16 * 1024);
210 }
211
212 errStream = channel.getErrStream();
213 }
214
215 @Override
216 public InputStream getInputStream() {
217 return inputStream;
218 }
219
220 @Override
221 public OutputStream getOutputStream() {
222 return outputStream;
223 }
224
225 @Override
226 public InputStream getErrorStream() {
227 return errStream;
228 }
229
230 @Override
231 public int exitValue() {
232 if (isRunning())
233 throw new IllegalStateException();
234 return channel.getExitStatus();
235 }
236
237 private boolean isRunning() {
238 return channel.getExitStatus() < 0 && channel.isConnected();
239 }
240
241 @Override
242 public void destroy() {
243 if (channel.isConnected())
244 channel.disconnect();
245 closeOutputStream();
246 }
247
248 @Override
249 public int waitFor() throws InterruptedException {
250 while (isRunning())
251 Thread.sleep(100);
252 return exitValue();
253 }
254 }
255
256 private class JschFtpChannel implements FtpChannel {
257
258 private ChannelSftp ftp;
259
260 @Override
261 public void connect(int timeout, TimeUnit unit) throws IOException {
262 try {
263 ftp = (ChannelSftp) sock.openChannel("sftp");
264 ftp.connect((int) unit.toMillis(timeout));
265 } catch (JSchException e) {
266 ftp = null;
267 throw new IOException(e.getLocalizedMessage(), e);
268 }
269 }
270
271 @Override
272 public void disconnect() {
273 ftp.disconnect();
274 ftp = null;
275 }
276
277 private <T> T map(Callable<T> op) throws IOException {
278 try {
279 return op.call();
280 } catch (Exception e) {
281 if (e instanceof SftpException) {
282 throw new FtpChannel.FtpException(e.getLocalizedMessage(),
283 ((SftpException) e).id, e);
284 }
285 throw new IOException(e.getLocalizedMessage(), e);
286 }
287 }
288
289 @Override
290 public boolean isConnected() {
291 return ftp != null && sock.isConnected();
292 }
293
294 @Override
295 public void cd(String path) throws IOException {
296 map(() -> {
297 ftp.cd(path);
298 return null;
299 });
300 }
301
302 @Override
303 public String pwd() throws IOException {
304 return map(() -> ftp.pwd());
305 }
306
307 @Override
308 public Collection<DirEntry> ls(String path) throws IOException {
309 return map(() -> {
310 List<DirEntry> result = new ArrayList<>();
311 for (Object e : ftp.ls(path)) {
312 ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) e;
313 result.add(new DirEntry() {
314
315 @Override
316 public String getFilename() {
317 return entry.getFilename();
318 }
319
320 @Override
321 public long getModifiedTime() {
322 return entry.getAttrs().getMTime();
323 }
324
325 @Override
326 public boolean isDirectory() {
327 return entry.getAttrs().isDir();
328 }
329 });
330 }
331 return result;
332 });
333 }
334
335 @Override
336 public void rmdir(String path) throws IOException {
337 map(() -> {
338 ftp.rm(path);
339 return null;
340 });
341 }
342
343 @Override
344 public void mkdir(String path) throws IOException {
345 map(() -> {
346 ftp.mkdir(path);
347 return null;
348 });
349 }
350
351 @Override
352 public InputStream get(String path) throws IOException {
353 return map(() -> ftp.get(path));
354 }
355
356 @Override
357 public OutputStream put(String path) throws IOException {
358 return map(() -> ftp.put(path));
359 }
360
361 @Override
362 public void rm(String path) throws IOException {
363 map(() -> {
364 ftp.rm(path);
365 return null;
366 });
367 }
368
369 @Override
370 public void rename(String from, String to) throws IOException {
371 map(() -> {
372
373
374
375 if (hasPosixRename()) {
376 ftp.rename(from, to);
377 } else if (!to.equals(from)) {
378
379
380
381
382
383 delete(to);
384 ftp.rename(from, to);
385 }
386 return null;
387 });
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401 private boolean hasPosixRename() {
402 return "1".equals(ftp.getExtension("posix-rename@openssh.com"));
403 }
404 }
405 }