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