~ubuntu-branches/ubuntu/raring/wagon2/raring-proposed

« back to all changes in this revision

Viewing changes to wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/AbstractJschWagon.java

  • Committer: Package Import Robot
  • Author(s): Damien Raude-Morvan
  • Date: 2012-01-29 23:23:22 UTC
  • Revision ID: package-import@ubuntu.com-20120129232322-w9h5j9c81zi8f23o
Tags: upstream-2.2
ImportĀ upstreamĀ versionĀ 2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.apache.maven.wagon.providers.ssh.jsch;
 
2
 
 
3
/*
 
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
 
11
 *
 
12
 *   http://www.apache.org/licenses/LICENSE-2.0
 
13
 *
 
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
 
19
 * under the License.
 
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
 * AbstractJschWagon 
 
72
 *
 
73
 * @version $Id: AbstractJschWagon.java 1170464 2011-09-14 07:56:38Z olamy $
 
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
     * @plexus.requirement role-hint="file"
 
85
     */
 
86
    private KnownHostsProvider knownHostsProvider;
 
87
    
 
88
    /**
 
89
     * @plexus.requirement
 
90
     */
 
91
    private InteractiveUserInfo interactiveUserInfo;
 
92
 
 
93
    /**
 
94
     * @plexus.requirement
 
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
                // Backwards compatibility
 
177
                proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
 
178
                if ( proxyInfo != null && proxyInfo.getHost() != null )
 
179
                {
 
180
                    // if port == 1080 we will use SOCKS5 Proxy, otherwise will use HTTP Proxy
 
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
        // username and password will be given via UserInfo interface.
 
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
                // continue without known_hosts
 
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
}