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