1 package org.apache.maven.wagon.providers.ssh.jsch;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.PrintWriter;
29 import java.io.StringWriter;
30 import java.util.List;
31 import java.util.Properties;
32
33 import org.apache.maven.wagon.CommandExecutionException;
34 import org.apache.maven.wagon.CommandExecutor;
35 import org.apache.maven.wagon.ResourceDoesNotExistException;
36 import org.apache.maven.wagon.StreamWagon;
37 import org.apache.maven.wagon.Streams;
38 import org.apache.maven.wagon.TransferFailedException;
39 import org.apache.maven.wagon.WagonConstants;
40 import org.apache.maven.wagon.authentication.AuthenticationException;
41 import org.apache.maven.wagon.authentication.AuthenticationInfo;
42 import org.apache.maven.wagon.authorization.AuthorizationException;
43 import org.apache.maven.wagon.events.TransferEvent;
44 import org.apache.maven.wagon.providers.ssh.CommandExecutorStreamProcessor;
45 import org.apache.maven.wagon.providers.ssh.ScpHelper;
46 import org.apache.maven.wagon.providers.ssh.SshWagon;
47 import org.apache.maven.wagon.providers.ssh.interactive.InteractiveUserInfo;
48 import org.apache.maven.wagon.providers.ssh.interactive.NullInteractiveUserInfo;
49 import org.apache.maven.wagon.providers.ssh.jsch.interactive.UserInfoUIKeyboardInteractiveProxy;
50 import org.apache.maven.wagon.providers.ssh.knownhost.KnownHostChangedException;
51 import org.apache.maven.wagon.providers.ssh.knownhost.KnownHostsProvider;
52 import org.apache.maven.wagon.providers.ssh.knownhost.UnknownHostException;
53 import org.apache.maven.wagon.proxy.ProxyInfo;
54 import org.apache.maven.wagon.resource.Resource;
55 import org.codehaus.plexus.util.IOUtil;
56 import org.codehaus.plexus.util.StringInputStream;
57
58 import com.jcraft.jsch.ChannelExec;
59 import com.jcraft.jsch.HostKey;
60 import com.jcraft.jsch.HostKeyRepository;
61 import com.jcraft.jsch.JSch;
62 import com.jcraft.jsch.JSchException;
63 import com.jcraft.jsch.Proxy;
64 import com.jcraft.jsch.ProxyHTTP;
65 import com.jcraft.jsch.ProxySOCKS5;
66 import com.jcraft.jsch.Session;
67 import com.jcraft.jsch.UIKeyboardInteractive;
68 import com.jcraft.jsch.UserInfo;
69
70
71
72
73
74
75 public abstract class AbstractJschWagon
76 extends StreamWagon
77 implements SshWagon, CommandExecutor
78 {
79 protected ScpHelper sshTool = new ScpHelper( this );
80
81 protected Session session;
82
83
84
85
86 private KnownHostsProvider knownHostsProvider;
87
88
89
90
91 private InteractiveUserInfo interactiveUserInfo;
92
93
94
95
96 private UIKeyboardInteractive uIKeyboardInteractive;
97
98 private static final int SOCKS5_PROXY_PORT = 1080;
99
100 protected static final String EXEC_CHANNEL = "exec";
101
102 public void openConnectionInternal()
103 throws AuthenticationException
104 {
105 if ( authenticationInfo == null )
106 {
107 authenticationInfo = new AuthenticationInfo();
108 }
109
110 if ( !interactive )
111 {
112 uIKeyboardInteractive = null;
113 setInteractiveUserInfo( new NullInteractiveUserInfo() );
114 }
115
116 JSch sch = new JSch();
117
118 File privateKey;
119 try
120 {
121 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
122 }
123 catch ( FileNotFoundException e )
124 {
125 throw new AuthenticationException( e.getMessage() );
126 }
127
128 if ( privateKey != null && privateKey.exists() )
129 {
130 fireSessionDebug( "Using private key: " + privateKey );
131 try
132 {
133 sch.addIdentity( privateKey.getAbsolutePath(), authenticationInfo.getPassphrase() );
134 }
135 catch ( JSchException e )
136 {
137 throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
138 }
139 }
140
141 String host = getRepository().getHost();
142 int port =
143 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
144 try
145 {
146 String userName = authenticationInfo.getUserName();
147 if ( userName == null )
148 {
149 userName = System.getProperty( "user.name" );
150 }
151 session = sch.getSession( userName, host, port );
152 session.setTimeout( getTimeout() );
153 }
154 catch ( JSchException e )
155 {
156 throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
157 }
158
159 Proxy proxy = null;
160 ProxyInfo proxyInfo = getProxyInfo( ProxyInfo.PROXY_SOCKS5, getRepository().getHost() );
161 if ( proxyInfo != null && proxyInfo.getHost() != null )
162 {
163 proxy = new ProxySOCKS5( proxyInfo.getHost(), proxyInfo.getPort() );
164 ( (ProxySOCKS5) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
165 }
166 else
167 {
168 proxyInfo = getProxyInfo( ProxyInfo.PROXY_HTTP, getRepository().getHost() );
169 if ( proxyInfo != null && proxyInfo.getHost() != null )
170 {
171 proxy = new ProxyHTTP( proxyInfo.getHost(), proxyInfo.getPort() );
172 ( (ProxyHTTP) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
173 }
174 else
175 {
176
177 proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
178 if ( proxyInfo != null && proxyInfo.getHost() != null )
179 {
180
181 if ( proxyInfo.getPort() == SOCKS5_PROXY_PORT )
182 {
183 proxy = new ProxySOCKS5( proxyInfo.getHost(), proxyInfo.getPort() );
184 ( (ProxySOCKS5) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
185 }
186 else
187 {
188 proxy = new ProxyHTTP( proxyInfo.getHost(), proxyInfo.getPort() );
189 ( (ProxyHTTP) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
190 }
191 }
192 }
193 }
194 session.setProxy( proxy );
195
196
197 UserInfo ui = new WagonUserInfo( authenticationInfo, getInteractiveUserInfo() );
198
199 if ( uIKeyboardInteractive != null )
200 {
201 ui = new UserInfoUIKeyboardInteractiveProxy( ui, uIKeyboardInteractive );
202 }
203
204 Properties config = new Properties();
205 if ( getKnownHostsProvider() != null )
206 {
207 try
208 {
209 String contents = getKnownHostsProvider().getContents();
210 if ( contents != null )
211 {
212 sch.setKnownHosts( new StringInputStream( contents ) );
213 }
214 }
215 catch ( JSchException e )
216 {
217
218 }
219 config.setProperty( "StrictHostKeyChecking", getKnownHostsProvider().getHostKeyChecking() );
220 }
221
222 if ( authenticationInfo.getPassword() != null )
223 {
224 config.setProperty( "PreferredAuthentications", "gssapi-with-mic,publickey,password,keyboard-interactive" );
225 }
226
227 config.setProperty( "BatchMode", interactive ? "no" : "yes" );
228
229 session.setConfig( config );
230
231 session.setUserInfo( ui );
232
233 StringWriter stringWriter = new StringWriter();
234 try
235 {
236 session.connect();
237
238 if ( getKnownHostsProvider() != null )
239 {
240 PrintWriter w = new PrintWriter( stringWriter );
241
242 HostKeyRepository hkr = sch.getHostKeyRepository();
243 HostKey[] keys = hkr.getHostKey();
244
245 for ( int i = 0; keys != null && i < keys.length; i++ )
246 {
247 HostKey key = keys[i];
248 w.println( key.getHost() + " " + key.getType() + " " + key.getKey() );
249 }
250 }
251 }
252 catch ( JSchException e )
253 {
254 if ( e.getMessage().startsWith( "UnknownHostKey:" ) || e.getMessage().startsWith( "reject HostKey:" ) )
255 {
256 throw new UnknownHostException( host, e );
257 }
258 else if ( e.getMessage().indexOf( "HostKey has been changed" ) >= 0 )
259 {
260 throw new KnownHostChangedException( host, e );
261 }
262 else
263 {
264 throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
265 }
266 }
267
268 try
269 {
270 getKnownHostsProvider().storeKnownHosts( stringWriter.toString() );
271 }
272 catch ( IOException e )
273 {
274 closeConnection();
275
276 throw new AuthenticationException(
277 "Connection aborted - failed to write to known_hosts. Reason: " + e.getMessage(), e );
278 }
279 }
280
281 public void closeConnection()
282 {
283 if ( session != null )
284 {
285 session.disconnect();
286 session = null;
287 }
288 }
289
290 public Streams executeCommand( String command, boolean ignoreFailures )
291 throws CommandExecutionException
292 {
293 ChannelExec channel = null;
294 BufferedReader stdoutReader = null;
295 BufferedReader stderrReader = null;
296 try
297 {
298 channel = (ChannelExec) session.openChannel( EXEC_CHANNEL );
299
300 channel.setCommand( command + "\n" );
301
302 InputStream stdout = channel.getInputStream();
303 InputStream stderr = channel.getErrStream();
304
305 channel.connect();
306
307 stdoutReader = new BufferedReader( new InputStreamReader( stdout ) );
308 stderrReader = new BufferedReader( new InputStreamReader( stderr ) );
309
310 Streams streams = CommandExecutorStreamProcessor.processStreams( stderrReader, stdoutReader );
311
312 if ( streams.getErr().length() > 0 && !ignoreFailures )
313 {
314 int exitCode = channel.getExitStatus();
315 throw new CommandExecutionException( "Exit code: " + exitCode + " - " + streams.getErr() );
316 }
317
318 return streams;
319 }
320 catch ( IOException e )
321 {
322 throw new CommandExecutionException( "Cannot execute remote command: " + command, e );
323 }
324 catch ( JSchException e )
325 {
326 throw new CommandExecutionException( "Cannot execute remote command: " + command, e );
327 }
328 finally
329 {
330 IOUtil.close( stdoutReader );
331 IOUtil.close( stderrReader );
332 if ( channel != null )
333 {
334 channel.disconnect();
335 }
336 }
337 }
338
339 protected void handleGetException( Resource resource, Exception e )
340 throws TransferFailedException
341 {
342 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
343
344 String msg =
345 "Error occurred while downloading '" + resource + "' from the remote repository:" + getRepository() + ": "
346 + e.getMessage();
347
348 throw new TransferFailedException( msg, e );
349 }
350
351 public List<String> getFileList( String destinationDirectory )
352 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
353 {
354 return sshTool.getFileList( destinationDirectory, repository );
355 }
356
357 public void putDirectory( File sourceDirectory, String destinationDirectory )
358 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
359 {
360 sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
361 }
362
363 public boolean resourceExists( String resourceName )
364 throws TransferFailedException, AuthorizationException
365 {
366 return sshTool.resourceExists( resourceName, repository );
367 }
368
369 public boolean supportsDirectoryCopy()
370 {
371 return true;
372 }
373
374 public void executeCommand( String command )
375 throws CommandExecutionException
376 {
377 fireTransferDebug( "Executing command: " + command );
378
379 executeCommand( command, false );
380 }
381
382 public InteractiveUserInfo getInteractiveUserInfo()
383 {
384 return this.interactiveUserInfo;
385 }
386
387 public KnownHostsProvider getKnownHostsProvider()
388 {
389 return this.knownHostsProvider;
390 }
391
392 public void setInteractiveUserInfo( InteractiveUserInfo interactiveUserInfo )
393 {
394 this.interactiveUserInfo = interactiveUserInfo;
395 }
396
397 public void setKnownHostsProvider( KnownHostsProvider knownHostsProvider )
398 {
399 this.knownHostsProvider = knownHostsProvider;
400 }
401 }