Coverage Report - org.apache.maven.wagon.providers.ssh.external.ScpExternalWagon
 
Classes in this File Line Coverage Branch Coverage Complexity
ScpExternalWagon
0 %
0/167
0 %
0/66
2,96
 
 1  
 package org.apache.maven.wagon.providers.ssh.external;
 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 org.apache.maven.wagon.AbstractWagon;
 23  
 import org.apache.maven.wagon.CommandExecutionException;
 24  
 import org.apache.maven.wagon.CommandExecutor;
 25  
 import org.apache.maven.wagon.PathUtils;
 26  
 import org.apache.maven.wagon.PermissionModeUtils;
 27  
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 28  
 import org.apache.maven.wagon.Streams;
 29  
 import org.apache.maven.wagon.TransferFailedException;
 30  
 import org.apache.maven.wagon.WagonConstants;
 31  
 import org.apache.maven.wagon.authentication.AuthenticationException;
 32  
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 33  
 import org.apache.maven.wagon.authorization.AuthorizationException;
 34  
 import org.apache.maven.wagon.events.TransferEvent;
 35  
 import org.apache.maven.wagon.providers.ssh.ScpHelper;
 36  
 import org.apache.maven.wagon.repository.RepositoryPermissions;
 37  
 import org.apache.maven.wagon.resource.Resource;
 38  
 import org.codehaus.plexus.util.StringUtils;
 39  
 import org.codehaus.plexus.util.cli.CommandLineException;
 40  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 41  
 import org.codehaus.plexus.util.cli.Commandline;
 42  
 
 43  
 import java.io.File;
 44  
 import java.io.FileNotFoundException;
 45  
 import java.util.List;
 46  
 import java.util.Locale;
 47  
 
 48  
 /**
 49  
  * SCP deployer using "external" scp program.  To allow for
 50  
  * ssh-agent type behavior, until we can construct a Java SSH Agent and interface for JSch.
 51  
  *
 52  
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 53  
  * @version $Id:ScpExternalWagon.java 477260 2006-11-20 17:11:39Z brett $
 54  
  * @todo [BP] add compression flag
 55  
  * @plexus.component role="org.apache.maven.wagon.Wagon"
 56  
  * role-hint="scpexe"
 57  
  * instantiation-strategy="per-lookup"
 58  
  */
 59  0
 public class ScpExternalWagon
 60  
     extends AbstractWagon
 61  
     implements CommandExecutor
 62  
 {
 63  
     /**
 64  
      * The external SCP command to use - default is <code>scp</code>.
 65  
      *
 66  
      * @component.configuration default="scp"
 67  
      */
 68  0
     private String scpExecutable = "scp";
 69  
 
 70  
     /**
 71  
      * The external SSH command to use - default is <code>ssh</code>.
 72  
      *
 73  
      * @component.configuration default="ssh"
 74  
      */
 75  0
     private String sshExecutable = "ssh";
 76  
 
 77  
     /**
 78  
      * Arguments to pass to the SCP command.
 79  
      *
 80  
      * @component.configuration
 81  
      */
 82  
     private String scpArgs;
 83  
 
 84  
     /**
 85  
      * Arguments to pass to the SSH command.
 86  
      *
 87  
      * @component.configuration
 88  
      */
 89  
     private String sshArgs;
 90  
 
 91  0
     private ScpHelper sshTool = new ScpHelper( this );
 92  
 
 93  
     private static final int SSH_FATAL_EXIT_CODE = 255;
 94  
 
 95  
     // ----------------------------------------------------------------------
 96  
     //
 97  
     // ----------------------------------------------------------------------
 98  
 
 99  
     protected void openConnectionInternal()
 100  
         throws AuthenticationException
 101  
     {
 102  0
         if ( authenticationInfo == null )
 103  
         {
 104  0
             authenticationInfo = new AuthenticationInfo();
 105  
         }
 106  0
     }
 107  
 
 108  
     public void closeConnection()
 109  
     {
 110  
         // nothing to disconnect
 111  0
     }
 112  
 
 113  
     public boolean getIfNewer( String resourceName, File destination, long timestamp )
 114  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 115  
     {
 116  0
         fireSessionDebug( "getIfNewer in SCP wagon is not supported - performing an unconditional get" );
 117  0
         get( resourceName, destination );
 118  0
         return true;
 119  
     }
 120  
 
 121  
     /**
 122  
      * @return The hostname of the remote server prefixed with the username, which comes either from the repository URL
 123  
      *         or from the authenticationInfo.
 124  
      */
 125  
     private String buildRemoteHost()
 126  
     {
 127  0
         String username = this.getRepository().getUsername();
 128  0
         if ( username == null )
 129  
         {
 130  0
             username = authenticationInfo.getUserName();
 131  
         }
 132  
 
 133  0
         if ( username == null )
 134  
         {
 135  0
             return getRepository().getHost();
 136  
         }
 137  
         else
 138  
         {
 139  0
             return username + "@" + getRepository().getHost();
 140  
         }
 141  
     }
 142  
 
 143  
     public void executeCommand( String command )
 144  
         throws CommandExecutionException
 145  
     {
 146  0
         fireTransferDebug( "Executing command: " + command );
 147  
 
 148  0
         executeCommand( command, false );
 149  0
     }
 150  
 
 151  
     public Streams executeCommand( String command, boolean ignoreFailures )
 152  
         throws CommandExecutionException
 153  
     {
 154  0
         boolean putty = isPuTTY();
 155  
 
 156  
         File privateKey;
 157  
         try
 158  
         {
 159  0
             privateKey = ScpHelper.getPrivateKey( authenticationInfo );
 160  
         }
 161  0
         catch ( FileNotFoundException e )
 162  
         {
 163  0
             throw new CommandExecutionException( e.getMessage(), e );
 164  0
         }
 165  0
         Commandline cl = createBaseCommandLine( putty, sshExecutable, privateKey );
 166  
 
 167  0
         int port =
 168  
             repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
 169  0
         if ( port != ScpHelper.DEFAULT_SSH_PORT )
 170  
         {
 171  0
             if ( putty )
 172  
             {
 173  0
                 cl.createArg().setLine( "-P " + port );
 174  
             }
 175  
             else
 176  
             {
 177  0
                 cl.createArg().setLine( "-p " + port );
 178  
             }
 179  
         }
 180  
 
 181  0
         if ( sshArgs != null )
 182  
         {
 183  0
             cl.createArg().setLine( sshArgs );
 184  
         }
 185  
 
 186  0
         String remoteHost = this.buildRemoteHost();
 187  
 
 188  0
         cl.createArg().setValue( remoteHost );
 189  
 
 190  0
         cl.createArg().setValue( command );
 191  
 
 192  0
         fireSessionDebug( "Executing command: " + cl.toString() );
 193  
 
 194  
         try
 195  
         {
 196  0
             CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
 197  0
             CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
 198  0
             int exitCode = CommandLineUtils.executeCommandLine( cl, out, err );
 199  0
             Streams streams = new Streams();
 200  0
             streams.setOut( out.getOutput() );
 201  0
             streams.setErr( err.getOutput() );
 202  0
             fireSessionDebug( streams.getOut() );
 203  0
             fireSessionDebug( streams.getErr() );
 204  0
             if ( exitCode != 0 )
 205  
             {
 206  0
                 if ( !ignoreFailures || exitCode == SSH_FATAL_EXIT_CODE )
 207  
                 {
 208  0
                     throw new CommandExecutionException( "Exit code " + exitCode + " - " + err.getOutput() );
 209  
                 }
 210  
             }
 211  0
             return streams;
 212  
         }
 213  0
         catch ( CommandLineException e )
 214  
         {
 215  0
             throw new CommandExecutionException( "Error executing command line", e );
 216  
         }
 217  
     }
 218  
 
 219  
     protected boolean isPuTTY()
 220  
     {
 221  0
         return sshExecutable.toLowerCase( Locale.ENGLISH ).indexOf( "plink" ) >= 0;
 222  
     }
 223  
 
 224  
     private Commandline createBaseCommandLine( boolean putty, String executable, File privateKey )
 225  
     {
 226  0
         Commandline cl = new Commandline();
 227  
 
 228  0
         cl.setExecutable( executable );
 229  
 
 230  0
         if ( privateKey != null )
 231  
         {
 232  0
             cl.createArg().setValue( "-i" );
 233  0
             cl.createArg().setFile( privateKey );
 234  
         }
 235  
 
 236  0
         String password = authenticationInfo.getPassword();
 237  0
         if ( putty && password != null )
 238  
         {
 239  0
             cl.createArg().setValue( "-pw" );
 240  0
             cl.createArg().setValue( password );
 241  
         }
 242  
 
 243  
         // should check interactive flag, but scpexe never works in interactive mode right now due to i/o streams
 244  0
         if ( putty )
 245  
         {
 246  0
             cl.createArg().setValue( "-batch" );
 247  
         }
 248  
         else
 249  
         {
 250  0
             cl.createArg().setValue( "-o" );
 251  0
             cl.createArg().setValue( "BatchMode yes" );
 252  
         }
 253  0
         return cl;
 254  
     }
 255  
 
 256  
 
 257  
     private void executeScpCommand( Resource resource, File localFile, boolean put )
 258  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 259  
     {
 260  0
         boolean putty = isPuTTYSCP();
 261  
 
 262  
         File privateKey;
 263  
         try
 264  
         {
 265  0
             privateKey = ScpHelper.getPrivateKey( authenticationInfo );
 266  
         }
 267  0
         catch ( FileNotFoundException e )
 268  
         {
 269  0
             fireSessionConnectionRefused();
 270  
 
 271  0
             throw new AuthorizationException( e.getMessage() );
 272  0
         }
 273  0
         Commandline cl = createBaseCommandLine( putty, scpExecutable, privateKey );
 274  
 
 275  0
         cl.setWorkingDirectory( localFile.getParentFile().getAbsolutePath() );
 276  
 
 277  0
         int port =
 278  
             repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
 279  0
         if ( port != ScpHelper.DEFAULT_SSH_PORT )
 280  
         {
 281  0
             cl.createArg().setLine( "-P " + port );
 282  
         }
 283  
 
 284  0
         if ( scpArgs != null )
 285  
         {
 286  0
             cl.createArg().setLine( scpArgs );
 287  
         }
 288  
 
 289  0
         String resourceName = normalizeResource( resource );
 290  0
         String remoteFile = getRepository().getBasedir() + "/" + resourceName;
 291  
 
 292  0
         remoteFile = StringUtils.replace( remoteFile, " ", "\\ " );
 293  
 
 294  0
         String qualifiedRemoteFile = this.buildRemoteHost() + ":" + remoteFile;
 295  0
         if ( put )
 296  
         {
 297  0
             cl.createArg().setValue( localFile.getName() );
 298  0
             cl.createArg().setValue( qualifiedRemoteFile );
 299  
         }
 300  
         else
 301  
         {
 302  0
             cl.createArg().setValue( qualifiedRemoteFile );
 303  0
             cl.createArg().setValue( localFile.getName() );
 304  
         }
 305  
 
 306  0
         fireSessionDebug( "Executing command: " + cl.toString() );
 307  
 
 308  
         try
 309  
         {
 310  0
             CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
 311  0
             int exitCode = CommandLineUtils.executeCommandLine( cl, null, err );
 312  0
             if ( exitCode != 0 )
 313  
             {
 314  0
                 if ( !put && err.getOutput().trim().toLowerCase( Locale.ENGLISH ).indexOf( "no such file or directory" )
 315  
                     != -1 )
 316  
                 {
 317  0
                     throw new ResourceDoesNotExistException( err.getOutput() );
 318  
                 }
 319  
                 else
 320  
                 {
 321  0
                     TransferFailedException e =
 322  
                         new TransferFailedException( "Exit code: " + exitCode + " - " + err.getOutput() );
 323  
 
 324  0
                     fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
 325  
 
 326  0
                     throw e;
 327  
                 }
 328  
             }
 329  
         }
 330  0
         catch ( CommandLineException e )
 331  
         {
 332  0
             fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
 333  
 
 334  0
             throw new TransferFailedException( "Error executing command line", e );
 335  0
         }
 336  0
     }
 337  
 
 338  
     boolean isPuTTYSCP()
 339  
     {
 340  0
         return scpExecutable.toLowerCase( Locale.ENGLISH ).indexOf( "pscp" ) >= 0;
 341  
     }
 342  
 
 343  
     private String normalizeResource( Resource resource )
 344  
     {
 345  0
         return StringUtils.replace( resource.getName(), "\\", "/" );
 346  
     }
 347  
 
 348  
     public void put( File source, String destination )
 349  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 350  
     {
 351  0
         Resource resource = new Resource( destination );
 352  
 
 353  0
         firePutInitiated( resource, source );
 354  
 
 355  0
         if ( !source.exists() )
 356  
         {
 357  0
             throw new ResourceDoesNotExistException( "Specified source file does not exist: " + source );
 358  
         }
 359  
 
 360  0
         String basedir = getRepository().getBasedir();
 361  
 
 362  0
         String resourceName = StringUtils.replace( destination, "\\", "/" );
 363  
 
 364  0
         String dir = PathUtils.dirname( resourceName );
 365  
 
 366  0
         dir = StringUtils.replace( dir, "\\", "/" );
 367  
 
 368  0
         String umaskCmd = null;
 369  0
         if ( getRepository().getPermissions() != null )
 370  
         {
 371  0
             String dirPerms = getRepository().getPermissions().getDirectoryMode();
 372  
 
 373  0
             if ( dirPerms != null )
 374  
             {
 375  0
                 umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
 376  
             }
 377  
         }
 378  
 
 379  0
         String mkdirCmd = "mkdir -p " + basedir + "/" + dir + "\n";
 380  
 
 381  0
         if ( umaskCmd != null )
 382  
         {
 383  0
             mkdirCmd = umaskCmd + "; " + mkdirCmd;
 384  
         }
 385  
 
 386  
         try
 387  
         {
 388  0
             executeCommand( mkdirCmd );
 389  
         }
 390  0
         catch ( CommandExecutionException e )
 391  
         {
 392  0
             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
 393  
 
 394  0
             throw new TransferFailedException( "Error executing command for transfer", e );
 395  0
         }
 396  
 
 397  0
         resource.setContentLength( source.length() );
 398  
 
 399  0
         resource.setLastModified( source.lastModified() );
 400  
 
 401  0
         firePutStarted( resource, source );
 402  
 
 403  0
         executeScpCommand( resource, source, true );
 404  
 
 405  0
         postProcessListeners( resource, source, TransferEvent.REQUEST_PUT );
 406  
 
 407  
         try
 408  
         {
 409  0
             RepositoryPermissions permissions = getRepository().getPermissions();
 410  
 
 411  0
             if ( permissions != null && permissions.getGroup() != null )
 412  
             {
 413  0
                 executeCommand( "chgrp -f " + permissions.getGroup() + " " + basedir + "/" + resourceName + "\n",
 414  
                                 true );
 415  
             }
 416  
 
 417  0
             if ( permissions != null && permissions.getFileMode() != null )
 418  
             {
 419  0
                 executeCommand( "chmod -f " + permissions.getFileMode() + " " + basedir + "/" + resourceName + "\n",
 420  
                                 true );
 421  
             }
 422  
         }
 423  0
         catch ( CommandExecutionException e )
 424  
         {
 425  0
             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
 426  
 
 427  0
             throw new TransferFailedException( "Error executing command for transfer", e );
 428  0
         }
 429  0
         firePutCompleted( resource, source );
 430  0
     }
 431  
 
 432  
     public void get( String resourceName, File destination )
 433  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 434  
     {
 435  0
         String path = StringUtils.replace( resourceName, "\\", "/" );
 436  
 
 437  0
         Resource resource = new Resource( path );
 438  
 
 439  0
         fireGetInitiated( resource, destination );
 440  
 
 441  0
         createParentDirectories( destination );
 442  
 
 443  0
         fireGetStarted( resource, destination );
 444  
 
 445  0
         executeScpCommand( resource, destination, false );
 446  
 
 447  0
         postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
 448  
 
 449  0
         fireGetCompleted( resource, destination );
 450  0
     }
 451  
 
 452  
     //
 453  
     // these parameters are user specific, so should not be read from the repository itself.
 454  
     // They can be configured by plexus, or directly on the instantiated object.
 455  
     // Alternatively, we may later accept a generic parameters argument to connect, or some other configure(Properties)
 456  
     // method on a Wagon.
 457  
     //
 458  
 
 459  
     public List<String> getFileList( String destinationDirectory )
 460  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 461  
     {
 462  0
         return sshTool.getFileList( destinationDirectory, repository );
 463  
     }
 464  
 
 465  
     public void putDirectory( File sourceDirectory, String destinationDirectory )
 466  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 467  
     {
 468  0
         sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
 469  0
     }
 470  
 
 471  
     public boolean resourceExists( String resourceName )
 472  
         throws TransferFailedException, AuthorizationException
 473  
     {
 474  0
         return sshTool.resourceExists( resourceName, repository );
 475  
     }
 476  
 
 477  
     public boolean supportsDirectoryCopy()
 478  
     {
 479  0
         return true;
 480  
     }
 481  
 
 482  
     public String getScpExecutable()
 483  
     {
 484  0
         return scpExecutable;
 485  
     }
 486  
 
 487  
     public void setScpExecutable( String scpExecutable )
 488  
     {
 489  0
         this.scpExecutable = scpExecutable;
 490  0
     }
 491  
 
 492  
     public String getSshExecutable()
 493  
     {
 494  0
         return sshExecutable;
 495  
     }
 496  
 
 497  
     public void setSshExecutable( String sshExecutable )
 498  
     {
 499  0
         this.sshExecutable = sshExecutable;
 500  0
     }
 501  
 
 502  
     public String getScpArgs()
 503  
     {
 504  0
         return scpArgs;
 505  
     }
 506  
 
 507  
     public void setScpArgs( String scpArgs )
 508  
     {
 509  0
         this.scpArgs = scpArgs;
 510  0
     }
 511  
 
 512  
     public String getSshArgs()
 513  
     {
 514  0
         return sshArgs;
 515  
     }
 516  
 
 517  
     public void setSshArgs( String sshArgs )
 518  
     {
 519  0
         this.sshArgs = sshArgs;
 520  0
     }
 521  
 }