Coverage Report - org.apache.maven.scm.provider.cvslib.cvsjava.util.CvsConnection
 
Classes in this File Line Coverage Branch Coverage Complexity
CvsConnection
41 %
68/163
23 %
18/78
4,533
 
 1  
 package org.apache.maven.scm.provider.cvslib.cvsjava.util;
 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.scm.log.ScmLogger;
 23  
 import org.codehaus.plexus.util.StringUtils;
 24  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 25  
 import org.netbeans.lib.cvsclient.CVSRoot;
 26  
 import org.netbeans.lib.cvsclient.Client;
 27  
 import org.netbeans.lib.cvsclient.admin.StandardAdminHandler;
 28  
 import org.netbeans.lib.cvsclient.command.Command;
 29  
 import org.netbeans.lib.cvsclient.command.CommandAbortedException;
 30  
 import org.netbeans.lib.cvsclient.command.CommandException;
 31  
 import org.netbeans.lib.cvsclient.command.GlobalOptions;
 32  
 import org.netbeans.lib.cvsclient.commandLine.CommandFactory;
 33  
 import org.netbeans.lib.cvsclient.commandLine.GetOpt;
 34  
 import org.netbeans.lib.cvsclient.connection.AbstractConnection;
 35  
 import org.netbeans.lib.cvsclient.connection.AuthenticationException;
 36  
 import org.netbeans.lib.cvsclient.connection.Connection;
 37  
 import org.netbeans.lib.cvsclient.connection.ConnectionFactory;
 38  
 import org.netbeans.lib.cvsclient.connection.PServerConnection;
 39  
 import org.netbeans.lib.cvsclient.connection.StandardScrambler;
 40  
 import org.netbeans.lib.cvsclient.event.CVSListener;
 41  
 
 42  
 import java.io.BufferedReader;
 43  
 import java.io.File;
 44  
 import java.io.FileReader;
 45  
 import java.io.IOException;
 46  
 
 47  
 /**
 48  
  * A Cvs connection that simulates a command line interface.
 49  
  *
 50  
  * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
 51  
  * @version $Id: CvsConnection.java 1306867 2012-03-29 13:45:10Z olamy $
 52  
  */
 53  
 public class CvsConnection
 54  
 {
 55  
 
 56  
     /**
 57  
      * The path to the repository on the server
 58  
      */
 59  
     @SuppressWarnings( "unused" )
 60  
     private String repository;
 61  
 
 62  
     /**
 63  
      * The local path to use to perform operations (the top level)
 64  
      */
 65  
     private String localPath;
 66  
 
 67  
     /**
 68  
      * The connection to the server
 69  
      */
 70  
     private Connection connection;
 71  
 
 72  
     /**
 73  
      * The client that manages interactions with the server
 74  
      */
 75  
     private Client client;
 76  
 
 77  
     /**
 78  
      * The global options being used. GlobalOptions are only global for a
 79  
      * particular command.
 80  
      */
 81  
     private GlobalOptions globalOptions;
 82  
 
 83  
     private CvsConnection()
 84  8
     {
 85  8
     }
 86  
 
 87  
     /**
 88  
      * Execute a configured CVS command
 89  
      *
 90  
      * @param command the command to execute
 91  
      * @throws CommandException if there is an error running the command
 92  
      */
 93  
     public boolean executeCommand( Command command )
 94  
         throws CommandException, AuthenticationException
 95  
     {
 96  8
         return client.executeCommand( command, globalOptions );
 97  
     }
 98  
 
 99  
     public void setRepository( String repository )
 100  
     {
 101  8
         this.repository = repository;
 102  8
     }
 103  
 
 104  
     public void setLocalPath( String localPath )
 105  
     {
 106  8
         this.localPath = localPath;
 107  8
     }
 108  
 
 109  
     public void setGlobalOptions( GlobalOptions globalOptions )
 110  
     {
 111  8
         this.globalOptions = globalOptions;
 112  8
     }
 113  
 
 114  
     /**
 115  
      * Creates the connection and the client and connects.
 116  
      */
 117  
     private void connect( CVSRoot root, String password )
 118  
         throws AuthenticationException, CommandAbortedException
 119  
     {
 120  8
         if ( CVSRoot.METHOD_EXT.equals( root.getMethod() ) )
 121  
         {
 122  0
             String cvsRsh = System.getProperty( "maven.scm.cvs.java.cvs_rsh" );
 123  0
             if ( cvsRsh == null )
 124  
             {
 125  
                 try
 126  
                 {
 127  0
                     cvsRsh = CommandLineUtils.getSystemEnvVars().getProperty( "CVS_RSH" );
 128  
                 }
 129  0
                 catch ( IOException e )
 130  
                 {
 131  
                     // we assume searching env var can't fail
 132  0
                 }
 133  
             }
 134  
 
 135  0
             if ( cvsRsh != null )
 136  
             {
 137  0
                 if ( cvsRsh.indexOf( ' ' ) < 0 )
 138  
                 {
 139  
                     //cvs_rsh should be 'rsh' or 'ssh'
 140  
                     //Complete the command to use
 141  0
                     String username = root.getUserName();
 142  0
                     if ( username == null )
 143  
                     {
 144  0
                         username = System.getProperty( "user.name" );
 145  
                     }
 146  
 
 147  0
                     cvsRsh += " " + username + "@" + root.getHostName() + " cvs server";
 148  
                 }
 149  
 
 150  0
                 AbstractConnection conn = new org.netbeans.lib.cvsclient.connection.ExtConnection( cvsRsh );
 151  0
                 conn.setRepository( root.getRepository() );
 152  0
                 connection = conn;
 153  0
             }
 154  
             else
 155  
             {
 156  0
                 connection = new ExtConnection( root );
 157  
             }
 158  0
         }
 159  
         else
 160  
         {
 161  8
             connection = ConnectionFactory.getConnection( root );
 162  8
             if ( CVSRoot.METHOD_PSERVER.equals( root.getMethod() ) )
 163  
             {
 164  0
                 ( (PServerConnection) connection ).setEncodedPassword( password );
 165  
             }
 166  
         }
 167  8
         connection.open();
 168  
 
 169  8
         client = new Client( connection, new StandardAdminHandler() );
 170  8
         client.setLocalPath( localPath );
 171  8
     }
 172  
 
 173  
     private void disconnect()
 174  
     {
 175  8
         if ( connection != null && connection.isOpen() )
 176  
         {
 177  
             try
 178  
             {
 179  8
                 connection.close();
 180  
             }
 181  0
             catch ( IOException e )
 182  
             {
 183  
                 //ignore
 184  8
             }
 185  
         }
 186  8
     }
 187  
 
 188  
     private void addListener( CVSListener listener )
 189  
     {
 190  8
         if ( client != null )
 191  
         {
 192  
             // add a listener to the client
 193  8
             client.getEventManager().addCVSListener( listener );
 194  
         }
 195  8
     }
 196  
 
 197  
     /**
 198  
      * Obtain the CVS root, either from the -D option cvs.root or from the CVS
 199  
      * directory
 200  
      *
 201  
      * @return the CVSRoot string
 202  
      */
 203  
     private static String getCVSRoot( String workingDir )
 204  
     {
 205  8
         String root = null;
 206  8
         BufferedReader r = null;
 207  8
         if ( workingDir == null )
 208  
         {
 209  0
             workingDir = System.getProperty( "user.dir" );
 210  
         }
 211  
         try
 212  
         {
 213  8
             File f = new File( workingDir );
 214  8
             File rootFile = new File( f, "CVS/Root" );
 215  8
             if ( rootFile.exists() )
 216  
             {
 217  3
                 r = new BufferedReader( new FileReader( rootFile ) );
 218  3
                 root = r.readLine();
 219  
             }
 220  
         }
 221  0
         catch ( IOException e )
 222  
         {
 223  
             // ignore
 224  
         }
 225  
         finally
 226  
         {
 227  0
             try
 228  
             {
 229  8
                 if ( r != null )
 230  
                 {
 231  3
                     r.close();
 232  
                 }
 233  
             }
 234  0
             catch ( IOException e )
 235  
             {
 236  0
                 System.err.println( "Warning: could not close CVS/Root file!" );
 237  8
             }
 238  0
         }
 239  8
         if ( root == null )
 240  
         {
 241  5
             root = System.getProperty( "cvs.root" );
 242  
         }
 243  8
         return root;
 244  
     }
 245  
 
 246  
     /**
 247  
      * Process global options passed into the application
 248  
      *
 249  
      * @param args          the argument list, complete
 250  
      * @param globalOptions the global options structure that will be passed to
 251  
      *                      the command
 252  
      */
 253  
     private static int processGlobalOptions( String[] args, GlobalOptions globalOptions )
 254  
     {
 255  8
         final String getOptString = globalOptions.getOptString();
 256  8
         GetOpt go = new GetOpt( args, getOptString );
 257  
         int ch;
 258  38
         while ( ( ch = go.getopt() ) != GetOpt.optEOF )
 259  
         {
 260  
             //System.out.println("Global option '"+((char) ch)+"',
 261  
             // '"+go.optArgGet()+"'");
 262  30
             String arg = go.optArgGet();
 263  30
             boolean success = globalOptions.setCVSCommand( (char) ch, arg );
 264  30
             if ( !success )
 265  
             {
 266  0
                 throw new IllegalArgumentException( "Failed to set CVS Command: -" + ch + " = " + arg );
 267  
             }
 268  30
         }
 269  
 
 270  8
         return go.optIndexGet();
 271  
     }
 272  
 
 273  
     /**
 274  
      * Lookup the password in the .cvspass file. This file is looked for in the
 275  
      * user.home directory if the option cvs.passfile is not set
 276  
      *
 277  
      * @param cvsRoot the CVS root for which the password is being searched
 278  
      * @return the password, scrambled
 279  
      */
 280  
     private static String lookupPassword( String cvsRoot, ScmLogger logger )
 281  
     {
 282  0
         File passFile = new File( System.getProperty( "cygwin.user.home", System.getProperty( "user.home" ) ) + File
 283  
             .separatorChar + ".cvspass" );
 284  
 
 285  0
         BufferedReader reader = null;
 286  0
         String password = null;
 287  
 
 288  
         try
 289  
         {
 290  0
             reader = new BufferedReader( new FileReader( passFile ) );
 291  0
             password = processCvspass( cvsRoot, reader );
 292  
         }
 293  0
         catch ( IOException e )
 294  
         {
 295  0
             if ( logger.isWarnEnabled() )
 296  
             {
 297  0
                 logger.warn( "Could not read password for '" + cvsRoot + "' from '" + passFile + "'", e );
 298  
             }
 299  0
             return null;
 300  
         }
 301  
         finally
 302  
         {
 303  0
             if ( reader != null )
 304  
             {
 305  
                 try
 306  
                 {
 307  0
                     reader.close();
 308  
                 }
 309  0
                 catch ( IOException e )
 310  
                 {
 311  0
                     if ( logger.isErrorEnabled() )
 312  
                     {
 313  0
                         logger.error( "Warning: could not close password file." );
 314  
                     }
 315  0
                 }
 316  
             }
 317  
         }
 318  0
         if ( password == null )
 319  
         {
 320  0
             if ( logger.isErrorEnabled() )
 321  
             {
 322  0
                 logger.error( "Didn't find password for CVSROOT '" + cvsRoot + "'." );
 323  
             }
 324  
         }
 325  0
         return password;
 326  
     }
 327  
 
 328  
     /**
 329  
      * Read in a list of return delimited lines from .cvspass and retreive
 330  
      * the password.  Return null if the cvsRoot can't be found.
 331  
      *
 332  
      * @param cvsRoot the CVS root for which the password is being searched
 333  
      * @param reader  A buffered reader of lines of cvspass information
 334  
      * @return The password, or null if it can't be found.
 335  
      * @throws IOException
 336  
      */
 337  
     static String processCvspass( String cvsRoot, BufferedReader reader )
 338  
         throws IOException
 339  
     {
 340  
         String line;
 341  0
         String password = null;
 342  0
         while ( ( line = reader.readLine() ) != null )
 343  
         {
 344  0
             if ( line.startsWith( "/" ) )
 345  
             {
 346  0
                 String[] cvspass = StringUtils.split( line, " " );
 347  0
                 String cvspassRoot = cvspass[1];
 348  0
                 if ( compareCvsRoot( cvsRoot, cvspassRoot ) )
 349  
                 {
 350  0
                     int index = line.indexOf( cvspassRoot ) + cvspassRoot.length() + 1;
 351  0
                     password = line.substring( index );
 352  0
                     break;
 353  
                 }
 354  0
             }
 355  0
             else if ( line.startsWith( cvsRoot ) )
 356  
             {
 357  0
                 password = line.substring( cvsRoot.length() + 1 );
 358  0
                 break;
 359  
             }
 360  
         }
 361  0
         return password;
 362  
     }
 363  
 
 364  
     static boolean compareCvsRoot( String cvsRoot, String target )
 365  
     {
 366  0
         String s1 = completeCvsRootPort( cvsRoot );
 367  0
         String s2 = completeCvsRootPort( target );
 368  0
         return s1 != null && s1.equals( s2 );
 369  
 
 370  
     }
 371  
 
 372  
     private static String completeCvsRootPort( String cvsRoot )
 373  
     {
 374  0
         String result = cvsRoot;
 375  0
         int idx = cvsRoot.indexOf( ':' );
 376  0
         for ( int i = 0; i < 2 && idx != -1; i++ )
 377  
         {
 378  0
             idx = cvsRoot.indexOf( ':', idx + 1 );
 379  
         }
 380  0
         if ( idx != -1 && cvsRoot.charAt( idx + 1 ) == '/' )
 381  
         {
 382  0
             StringBuilder sb = new StringBuilder();
 383  0
             sb.append( cvsRoot.substring( 0, idx + 1 ) );
 384  0
             sb.append( "2401" );
 385  0
             sb.append( cvsRoot.substring( idx + 1 ) );
 386  0
             result = sb.toString();
 387  
         }
 388  0
         return result;
 389  
 
 390  
     }
 391  
 
 392  
     /**
 393  
      * Process the CVS command passed in args[] array with all necessary
 394  
      * options. The only difference from main() method is, that this method
 395  
      * does not exit the JVM and provides command output.
 396  
      *
 397  
      * @param args The command with options
 398  
      */
 399  
     public static boolean processCommand( String[] args, String localPath, CVSListener listener, ScmLogger logger )
 400  
         throws Exception
 401  
     {
 402  
         // Set up the CVSRoot. Note that it might still be null after this
 403  
         // call if the user has decided to set it with the -d command line
 404  
         // global option
 405  8
         GlobalOptions globalOptions = new GlobalOptions();
 406  8
         globalOptions.setCVSRoot( getCVSRoot( localPath ) );
 407  
 
 408  
         // Set up any global options specified. These occur before the
 409  
         // name of the command to run
 410  
         int commandIndex;
 411  
 
 412  
         try
 413  
         {
 414  8
             commandIndex = processGlobalOptions( args, globalOptions );
 415  
         }
 416  0
         catch ( IllegalArgumentException e )
 417  
         {
 418  0
             if ( logger.isErrorEnabled() )
 419  
             {
 420  0
                 logger.error( "Invalid argument: " + e );
 421  
             }
 422  0
             return false;
 423  8
         }
 424  
 
 425  
         // if we don't have a CVS root by now, the user has messed up
 426  8
         if ( globalOptions.getCVSRoot() == null )
 427  
         {
 428  0
             if ( logger.isErrorEnabled() )
 429  
             {
 430  0
                 logger.error( "No CVS root is set. Check your <repository> information in the POM." );
 431  
             }
 432  0
             return false;
 433  
         }
 434  
 
 435  
         // parse the CVS root into its constituent parts
 436  
         CVSRoot root;
 437  8
         final String cvsRoot = globalOptions.getCVSRoot();
 438  
         try
 439  
         {
 440  8
             root = CVSRoot.parse( cvsRoot );
 441  
         }
 442  0
         catch ( IllegalArgumentException e )
 443  
         {
 444  0
             if ( logger.isErrorEnabled() )
 445  
             {
 446  0
                 logger.error( "Incorrect format for CVSRoot: " + cvsRoot + "\nThe correct format is: "
 447  
                     + "[:method:][[user][:password]@][hostname:[port]]/path/to/repository"
 448  
                     + "\nwhere \"method\" is pserver." );
 449  
             }
 450  0
             return false;
 451  8
         }
 452  
 
 453  8
         final String command = args[commandIndex];
 454  
 
 455  
         // this is not login, but a 'real' cvs command, so construct it,
 456  
         // set the options, and then connect to the server and execute it
 457  
 
 458  
         Command c;
 459  
         try
 460  
         {
 461  8
             c = CommandFactory.getDefault().createCommand( command, args, ++commandIndex, globalOptions, localPath );
 462  
         }
 463  0
         catch ( IllegalArgumentException e )
 464  
         {
 465  0
             if ( logger.isErrorEnabled() )
 466  
             {
 467  0
                 logger.error( "Illegal argument: " + e.getMessage() );
 468  
             }
 469  0
             return false;
 470  8
         }
 471  
 
 472  8
         String password = null;
 473  
 
 474  8
         if ( CVSRoot.METHOD_PSERVER.equals( root.getMethod() ) )
 475  
         {
 476  0
             password = root.getPassword();
 477  0
             if ( password != null )
 478  
             {
 479  0
                 password = StandardScrambler.getInstance().scramble( password );
 480  
             }
 481  
             else
 482  
             {
 483  0
                 password = lookupPassword( cvsRoot, logger );
 484  0
                 if ( password == null )
 485  
                 {
 486  0
                     password = StandardScrambler.getInstance().scramble( "" );
 487  
                     // an empty password
 488  
                 }
 489  
             }
 490  
         }
 491  8
         CvsConnection cvsCommand = new CvsConnection();
 492  8
         cvsCommand.setGlobalOptions( globalOptions );
 493  8
         cvsCommand.setRepository( root.getRepository() );
 494  
         // the local path is just the path where we executed the
 495  
         // command. This is the case for command-line CVS but not
 496  
         // usually for GUI front-ends
 497  8
         cvsCommand.setLocalPath( localPath );
 498  
 
 499  8
         cvsCommand.connect( root, password );
 500  8
         cvsCommand.addListener( listener );
 501  8
         if ( logger.isDebugEnabled() )
 502  
         {
 503  0
             logger.debug( "Executing CVS command: " + c.getCVSCommand() );
 504  
         }
 505  8
         boolean result = cvsCommand.executeCommand( c );
 506  8
         cvsCommand.disconnect();
 507  8
         return result;
 508  
     }
 509  
 }