1
package org.apache.maven.wagon.providers.ssh.jsch;
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
22
import java.io.BufferedReader;
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;
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;
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;
73
* @version $Id: AbstractJschWagon.java 1170464 2011-09-14 07:56:38Z olamy $
75
public abstract class AbstractJschWagon
77
implements SshWagon, CommandExecutor
79
protected ScpHelper sshTool = new ScpHelper( this );
81
protected Session session;
84
* @plexus.requirement role-hint="file"
86
private KnownHostsProvider knownHostsProvider;
91
private InteractiveUserInfo interactiveUserInfo;
96
private UIKeyboardInteractive uIKeyboardInteractive;
98
private static final int SOCKS5_PROXY_PORT = 1080;
100
protected static final String EXEC_CHANNEL = "exec";
102
public void openConnectionInternal()
103
throws AuthenticationException
105
if ( authenticationInfo == null )
107
authenticationInfo = new AuthenticationInfo();
112
uIKeyboardInteractive = null;
113
setInteractiveUserInfo( new NullInteractiveUserInfo() );
116
JSch sch = new JSch();
121
privateKey = ScpHelper.getPrivateKey( authenticationInfo );
123
catch ( FileNotFoundException e )
125
throw new AuthenticationException( e.getMessage() );
128
if ( privateKey != null && privateKey.exists() )
130
fireSessionDebug( "Using private key: " + privateKey );
133
sch.addIdentity( privateKey.getAbsolutePath(), authenticationInfo.getPassphrase() );
135
catch ( JSchException e )
137
throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
141
String host = getRepository().getHost();
143
repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
146
String userName = authenticationInfo.getUserName();
147
if ( userName == null )
149
userName = System.getProperty( "user.name" );
151
session = sch.getSession( userName, host, port );
152
session.setTimeout( getTimeout() );
154
catch ( JSchException e )
156
throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
160
ProxyInfo proxyInfo = getProxyInfo( ProxyInfo.PROXY_SOCKS5, getRepository().getHost() );
161
if ( proxyInfo != null && proxyInfo.getHost() != null )
163
proxy = new ProxySOCKS5( proxyInfo.getHost(), proxyInfo.getPort() );
164
( (ProxySOCKS5) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
168
proxyInfo = getProxyInfo( ProxyInfo.PROXY_HTTP, getRepository().getHost() );
169
if ( proxyInfo != null && proxyInfo.getHost() != null )
171
proxy = new ProxyHTTP( proxyInfo.getHost(), proxyInfo.getPort() );
172
( (ProxyHTTP) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
176
// Backwards compatibility
177
proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
178
if ( proxyInfo != null && proxyInfo.getHost() != null )
180
// if port == 1080 we will use SOCKS5 Proxy, otherwise will use HTTP Proxy
181
if ( proxyInfo.getPort() == SOCKS5_PROXY_PORT )
183
proxy = new ProxySOCKS5( proxyInfo.getHost(), proxyInfo.getPort() );
184
( (ProxySOCKS5) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
188
proxy = new ProxyHTTP( proxyInfo.getHost(), proxyInfo.getPort() );
189
( (ProxyHTTP) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
194
session.setProxy( proxy );
196
// username and password will be given via UserInfo interface.
197
UserInfo ui = new WagonUserInfo( authenticationInfo, getInteractiveUserInfo() );
199
if ( uIKeyboardInteractive != null )
201
ui = new UserInfoUIKeyboardInteractiveProxy( ui, uIKeyboardInteractive );
204
Properties config = new Properties();
205
if ( getKnownHostsProvider() != null )
209
String contents = getKnownHostsProvider().getContents();
210
if ( contents != null )
212
sch.setKnownHosts( new StringInputStream( contents ) );
215
catch ( JSchException e )
217
// continue without known_hosts
219
config.setProperty( "StrictHostKeyChecking", getKnownHostsProvider().getHostKeyChecking() );
222
if ( authenticationInfo.getPassword() != null )
224
config.setProperty( "PreferredAuthentications", "gssapi-with-mic,publickey,password,keyboard-interactive" );
227
config.setProperty( "BatchMode", interactive ? "no" : "yes" );
229
session.setConfig( config );
231
session.setUserInfo( ui );
233
StringWriter stringWriter = new StringWriter();
238
if ( getKnownHostsProvider() != null )
240
PrintWriter w = new PrintWriter( stringWriter );
242
HostKeyRepository hkr = sch.getHostKeyRepository();
243
HostKey[] keys = hkr.getHostKey();
245
for ( int i = 0; keys != null && i < keys.length; i++ )
247
HostKey key = keys[i];
248
w.println( key.getHost() + " " + key.getType() + " " + key.getKey() );
252
catch ( JSchException e )
254
if ( e.getMessage().startsWith( "UnknownHostKey:" ) || e.getMessage().startsWith( "reject HostKey:" ) )
256
throw new UnknownHostException( host, e );
258
else if ( e.getMessage().indexOf( "HostKey has been changed" ) >= 0 )
260
throw new KnownHostChangedException( host, e );
264
throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
270
getKnownHostsProvider().storeKnownHosts( stringWriter.toString() );
272
catch ( IOException e )
276
throw new AuthenticationException(
277
"Connection aborted - failed to write to known_hosts. Reason: " + e.getMessage(), e );
281
public void closeConnection()
283
if ( session != null )
285
session.disconnect();
290
public Streams executeCommand( String command, boolean ignoreFailures )
291
throws CommandExecutionException
293
ChannelExec channel = null;
294
BufferedReader stdoutReader = null;
295
BufferedReader stderrReader = null;
298
channel = (ChannelExec) session.openChannel( EXEC_CHANNEL );
300
channel.setCommand( command + "\n" );
302
InputStream stdout = channel.getInputStream();
303
InputStream stderr = channel.getErrStream();
307
stdoutReader = new BufferedReader( new InputStreamReader( stdout ) );
308
stderrReader = new BufferedReader( new InputStreamReader( stderr ) );
310
Streams streams = CommandExecutorStreamProcessor.processStreams( stderrReader, stdoutReader );
312
if ( streams.getErr().length() > 0 && !ignoreFailures )
314
int exitCode = channel.getExitStatus();
315
throw new CommandExecutionException( "Exit code: " + exitCode + " - " + streams.getErr() );
320
catch ( IOException e )
322
throw new CommandExecutionException( "Cannot execute remote command: " + command, e );
324
catch ( JSchException e )
326
throw new CommandExecutionException( "Cannot execute remote command: " + command, e );
330
IOUtil.close( stdoutReader );
331
IOUtil.close( stderrReader );
332
if ( channel != null )
334
channel.disconnect();
339
protected void handleGetException( Resource resource, Exception e )
340
throws TransferFailedException
342
fireTransferError( resource, e, TransferEvent.REQUEST_GET );
345
"Error occurred while downloading '" + resource + "' from the remote repository:" + getRepository() + ": "
348
throw new TransferFailedException( msg, e );
351
public List<String> getFileList( String destinationDirectory )
352
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
354
return sshTool.getFileList( destinationDirectory, repository );
357
public void putDirectory( File sourceDirectory, String destinationDirectory )
358
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
360
sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
363
public boolean resourceExists( String resourceName )
364
throws TransferFailedException, AuthorizationException
366
return sshTool.resourceExists( resourceName, repository );
369
public boolean supportsDirectoryCopy()
374
public void executeCommand( String command )
375
throws CommandExecutionException
377
fireTransferDebug( "Executing command: " + command );
379
executeCommand( command, false );
382
public InteractiveUserInfo getInteractiveUserInfo()
384
return this.interactiveUserInfo;
387
public KnownHostsProvider getKnownHostsProvider()
389
return this.knownHostsProvider;
392
public void setInteractiveUserInfo( InteractiveUserInfo interactiveUserInfo )
394
this.interactiveUserInfo = interactiveUserInfo;
397
public void setKnownHostsProvider( KnownHostsProvider knownHostsProvider )
399
this.knownHostsProvider = knownHostsProvider;