Coverage Report - org.apache.maven.wagon.providers.ssh.jsch.AbstractJschWagon
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractJschWagon
0 %
0/127
0 %
0/56
4,692
 
 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  0
 public abstract class AbstractJschWagon
 76  
     extends StreamWagon
 77  
     implements SshWagon, CommandExecutor
 78  
 {
 79  0
     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  0
         if ( authenticationInfo == null )
 106  
         {
 107  0
             authenticationInfo = new AuthenticationInfo();
 108  
         }
 109  
 
 110  0
         if ( !interactive )
 111  
         {
 112  0
             uIKeyboardInteractive = null;
 113  0
             setInteractiveUserInfo( new NullInteractiveUserInfo() );
 114  
         }
 115  
 
 116  0
         JSch sch = new JSch();
 117  
 
 118  
         File privateKey;
 119  
         try
 120  
         {
 121  0
             privateKey = ScpHelper.getPrivateKey( authenticationInfo );
 122  
         }
 123  0
         catch ( FileNotFoundException e )
 124  
         {
 125  0
             throw new AuthenticationException( e.getMessage() );
 126  0
         }
 127  
 
 128  0
         if ( privateKey != null && privateKey.exists() )
 129  
         {
 130  0
             fireSessionDebug( "Using private key: " + privateKey );
 131  
             try
 132  
             {
 133  0
                 sch.addIdentity( privateKey.getAbsolutePath(), authenticationInfo.getPassphrase() );
 134  
             }
 135  0
             catch ( JSchException e )
 136  
             {
 137  0
                 throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
 138  0
             }
 139  
         }
 140  
 
 141  0
         String host = getRepository().getHost();
 142  0
         int port =
 143  
             repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
 144  
         try
 145  
         {
 146  0
             String userName = authenticationInfo.getUserName();
 147  0
             if ( userName == null )
 148  
             {
 149  0
                 userName = System.getProperty( "user.name" );
 150  
             }
 151  0
             session = sch.getSession( userName, host, port );
 152  0
             session.setTimeout( getTimeout() );
 153  
         }
 154  0
         catch ( JSchException e )
 155  
         {
 156  0
             throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
 157  0
         }
 158  
 
 159  0
         Proxy proxy = null;
 160  0
         ProxyInfo proxyInfo = getProxyInfo( ProxyInfo.PROXY_SOCKS5, getRepository().getHost() );
 161  0
         if ( proxyInfo != null && proxyInfo.getHost() != null )
 162  
         {
 163  0
             proxy = new ProxySOCKS5( proxyInfo.getHost(), proxyInfo.getPort() );
 164  0
             ( (ProxySOCKS5) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
 165  
         }
 166  
         else
 167  
         {
 168  0
             proxyInfo = getProxyInfo( ProxyInfo.PROXY_HTTP, getRepository().getHost() );
 169  0
             if ( proxyInfo != null && proxyInfo.getHost() != null )
 170  
             {
 171  0
                 proxy = new ProxyHTTP( proxyInfo.getHost(), proxyInfo.getPort() );
 172  0
                 ( (ProxyHTTP) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
 173  
             }
 174  
             else
 175  
             {
 176  
                 // Backwards compatibility
 177  0
                 proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
 178  0
                 if ( proxyInfo != null && proxyInfo.getHost() != null )
 179  
                 {
 180  
                     // if port == 1080 we will use SOCKS5 Proxy, otherwise will use HTTP Proxy
 181  0
                     if ( proxyInfo.getPort() == SOCKS5_PROXY_PORT )
 182  
                     {
 183  0
                         proxy = new ProxySOCKS5( proxyInfo.getHost(), proxyInfo.getPort() );
 184  0
                         ( (ProxySOCKS5) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
 185  
                     }
 186  
                     else
 187  
                     {
 188  0
                         proxy = new ProxyHTTP( proxyInfo.getHost(), proxyInfo.getPort() );
 189  0
                         ( (ProxyHTTP) proxy ).setUserPasswd( proxyInfo.getUserName(), proxyInfo.getPassword() );
 190  
                     }
 191  
                 }
 192  
             }
 193  
         }
 194  0
         session.setProxy( proxy );
 195  
 
 196  
         // username and password will be given via UserInfo interface.
 197  0
         UserInfo ui = new WagonUserInfo( authenticationInfo, getInteractiveUserInfo() );
 198  
 
 199  0
         if ( uIKeyboardInteractive != null )
 200  
         {
 201  0
             ui = new UserInfoUIKeyboardInteractiveProxy( ui, uIKeyboardInteractive );
 202  
         }
 203  
 
 204  0
         Properties config = new Properties();
 205  0
         if ( getKnownHostsProvider() != null )
 206  
         {
 207  
             try
 208  
             {
 209  0
                 String contents = getKnownHostsProvider().getContents();
 210  0
                 if ( contents != null )
 211  
                 {
 212  0
                     sch.setKnownHosts( new StringInputStream( contents ) );
 213  
                 }
 214  
             }
 215  0
             catch ( JSchException e )
 216  
             {
 217  
                 // continue without known_hosts
 218  0
             }
 219  0
             config.setProperty( "StrictHostKeyChecking", getKnownHostsProvider().getHostKeyChecking() );
 220  
         }
 221  
 
 222  0
         if ( authenticationInfo.getPassword() != null )
 223  
         {
 224  0
             config.setProperty( "PreferredAuthentications", "gssapi-with-mic,publickey,password,keyboard-interactive" );
 225  
         }
 226  
         
 227  0
         config.setProperty( "BatchMode", interactive ? "no" : "yes" );
 228  
 
 229  0
         session.setConfig( config );
 230  
 
 231  0
         session.setUserInfo( ui );
 232  
 
 233  0
         StringWriter stringWriter = new StringWriter();
 234  
         try
 235  
         {
 236  0
             session.connect();
 237  
 
 238  0
             if ( getKnownHostsProvider() != null )
 239  
             {
 240  0
                 PrintWriter w = new PrintWriter( stringWriter );
 241  
 
 242  0
                 HostKeyRepository hkr = sch.getHostKeyRepository();
 243  0
                 HostKey[] keys = hkr.getHostKey();
 244  
 
 245  0
                 for ( int i = 0; keys != null && i < keys.length; i++ )
 246  
                 {
 247  0
                     HostKey key = keys[i];
 248  0
                     w.println( key.getHost() + " " + key.getType() + " " + key.getKey() );
 249  
                 }
 250  
             }
 251  
         }
 252  0
         catch ( JSchException e )
 253  
         {
 254  0
             if ( e.getMessage().startsWith( "UnknownHostKey:" ) || e.getMessage().startsWith( "reject HostKey:" ) )
 255  
             {
 256  0
                 throw new UnknownHostException( host, e );
 257  
             }
 258  0
             else if ( e.getMessage().indexOf( "HostKey has been changed" ) >= 0 )
 259  
             {
 260  0
                 throw new KnownHostChangedException( host, e );
 261  
             }
 262  
             else
 263  
             {
 264  0
                 throw new AuthenticationException( "Cannot connect. Reason: " + e.getMessage(), e );
 265  
             }
 266  0
         }
 267  
 
 268  
         try
 269  
         {
 270  0
             getKnownHostsProvider().storeKnownHosts( stringWriter.toString() );
 271  
         }
 272  0
         catch ( IOException e )
 273  
         {
 274  0
             closeConnection();
 275  
 
 276  0
             throw new AuthenticationException(
 277  
                 "Connection aborted - failed to write to known_hosts. Reason: " + e.getMessage(), e );
 278  0
         }
 279  0
     }
 280  
 
 281  
     public void closeConnection()
 282  
     {
 283  0
         if ( session != null )
 284  
         {
 285  0
             session.disconnect();
 286  0
             session = null;
 287  
         }
 288  0
     }
 289  
 
 290  
     public Streams executeCommand( String command, boolean ignoreFailures )
 291  
         throws CommandExecutionException
 292  
     {
 293  0
         ChannelExec channel = null;
 294  0
         BufferedReader stdoutReader = null;
 295  0
         BufferedReader stderrReader = null;
 296  
         try
 297  
         {
 298  0
             channel = (ChannelExec) session.openChannel( EXEC_CHANNEL );
 299  
 
 300  0
             channel.setCommand( command + "\n" );
 301  
 
 302  0
             InputStream stdout = channel.getInputStream();
 303  0
             InputStream stderr = channel.getErrStream();
 304  
 
 305  0
             channel.connect();
 306  
 
 307  0
             stdoutReader = new BufferedReader( new InputStreamReader( stdout ) );
 308  0
             stderrReader = new BufferedReader( new InputStreamReader( stderr ) );
 309  
 
 310  0
             Streams streams = CommandExecutorStreamProcessor.processStreams( stderrReader, stdoutReader );
 311  
 
 312  0
             if ( streams.getErr().length() > 0 && !ignoreFailures )
 313  
             {
 314  0
                 int exitCode = channel.getExitStatus();
 315  0
                 throw new CommandExecutionException( "Exit code: " + exitCode + " - " + streams.getErr() );
 316  
             }
 317  
 
 318  0
             return streams;
 319  
         }
 320  0
         catch ( IOException e )
 321  
         {
 322  0
             throw new CommandExecutionException( "Cannot execute remote command: " + command, e );
 323  
         }
 324  0
         catch ( JSchException e )
 325  
         {
 326  0
             throw new CommandExecutionException( "Cannot execute remote command: " + command, e );
 327  
         }
 328  
         finally
 329  
         {
 330  0
             IOUtil.close( stdoutReader );
 331  0
             IOUtil.close( stderrReader );
 332  0
             if ( channel != null )
 333  
             {
 334  0
                 channel.disconnect();
 335  
             }
 336  
         }
 337  
     }
 338  
 
 339  
     protected void handleGetException( Resource resource, Exception e )
 340  
         throws TransferFailedException
 341  
     {
 342  0
         fireTransferError( resource, e, TransferEvent.REQUEST_GET );
 343  
 
 344  0
         String msg =
 345  
             "Error occurred while downloading '" + resource + "' from the remote repository:" + getRepository() + ": "
 346  
                 + e.getMessage();
 347  
 
 348  0
         throw new TransferFailedException( msg, e );
 349  
     }
 350  
 
 351  
     public List<String> getFileList( String destinationDirectory )
 352  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 353  
     {
 354  0
         return sshTool.getFileList( destinationDirectory, repository );
 355  
     }
 356  
 
 357  
     public void putDirectory( File sourceDirectory, String destinationDirectory )
 358  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 359  
     {
 360  0
         sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
 361  0
     }
 362  
 
 363  
     public boolean resourceExists( String resourceName )
 364  
         throws TransferFailedException, AuthorizationException
 365  
     {
 366  0
         return sshTool.resourceExists( resourceName, repository );
 367  
     }
 368  
 
 369  
     public boolean supportsDirectoryCopy()
 370  
     {
 371  0
         return true;
 372  
     }
 373  
 
 374  
     public void executeCommand( String command )
 375  
         throws CommandExecutionException
 376  
     {
 377  0
         fireTransferDebug( "Executing command: " + command );
 378  
 
 379  0
         executeCommand( command, false );
 380  0
     }
 381  
 
 382  
     public InteractiveUserInfo getInteractiveUserInfo()
 383  
     {
 384  0
         return this.interactiveUserInfo;
 385  
     }
 386  
 
 387  
     public KnownHostsProvider getKnownHostsProvider()
 388  
     {
 389  0
         return this.knownHostsProvider;
 390  
     }
 391  
 
 392  
     public void setInteractiveUserInfo( InteractiveUserInfo interactiveUserInfo )
 393  
     {
 394  0
         this.interactiveUserInfo = interactiveUserInfo;
 395  0
     }
 396  
 
 397  
     public void setKnownHostsProvider( KnownHostsProvider knownHostsProvider )
 398  
     {
 399  0
         this.knownHostsProvider = knownHostsProvider;
 400  0
     }
 401  
 }