1 package org.apache.maven.scm.provider.cvslib;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.scm.CommandParameters;
23 import org.apache.maven.scm.ScmException;
24 import org.apache.maven.scm.ScmFileSet;
25 import org.apache.maven.scm.ScmResult;
26 import org.apache.maven.scm.command.Command;
27 import org.apache.maven.scm.command.add.AddScmResult;
28 import org.apache.maven.scm.command.branch.BranchScmResult;
29 import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
30 import org.apache.maven.scm.command.checkin.CheckInScmResult;
31 import org.apache.maven.scm.command.checkout.CheckOutScmResult;
32 import org.apache.maven.scm.command.diff.DiffScmResult;
33 import org.apache.maven.scm.command.export.ExportScmResult;
34 import org.apache.maven.scm.command.list.ListScmResult;
35 import org.apache.maven.scm.command.login.LoginScmResult;
36 import org.apache.maven.scm.command.remove.RemoveScmResult;
37 import org.apache.maven.scm.command.status.StatusScmResult;
38 import org.apache.maven.scm.command.tag.TagScmResult;
39 import org.apache.maven.scm.command.update.UpdateScmResult;
40 import org.apache.maven.scm.provider.AbstractScmProvider;
41 import org.apache.maven.scm.provider.ScmProviderRepository;
42 import org.apache.maven.scm.provider.cvslib.repository.CvsScmProviderRepository;
43 import org.apache.maven.scm.repository.ScmRepositoryException;
44 import org.apache.maven.scm.repository.UnknownRepositoryStructure;
45 import org.codehaus.plexus.util.FileUtils;
46 import org.codehaus.plexus.util.StringUtils;
47
48 import java.io.File;
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.List;
52
53
54
55
56
57
58 public abstract class AbstractCvsScmProvider
59 extends AbstractScmProvider
60 {
61
62 public final static String TRANSPORT_LOCAL = "local";
63
64
65 public final static String TRANSPORT_PSERVER = "pserver";
66
67
68 public final static String TRANSPORT_LSERVER = "lserver";
69
70
71 public final static String TRANSPORT_EXT = "ext";
72
73
74 public final static String TRANSPORT_SSPI = "sspi";
75
76
77
78
79
80 private static class ScmUrlParserResult
81 {
82 List messages = new ArrayList();
83
84 ScmProviderRepository repository;
85 }
86
87
88
89
90
91 public String getScmSpecificFilename()
92 {
93 return "CVS";
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public String sanitizeTagName( String arg0 )
111 {
112 if ( validateTagName( arg0 ) )
113 {
114 return arg0;
115 }
116
117 if ( arg0.equals( "HEAD" ) || arg0.equals( "BASE" ) || !arg0.matches( "[A-Za-z].*" ) )
118
119 {
120 throw new RuntimeException(
121 "Unable to sanitize tag " + arg0 + ": must begin with a letter" + "and not be HEAD or BASE" );
122 }
123
124
125 return arg0.replaceAll( "[^A-Za-z0-9_-]", "_" );
126 }
127
128
129
130
131 public boolean validateTagName( String arg0 )
132 {
133 return ( arg0.matches( "[A-Za-z][A-Za-z0-9_-]*" ) && !arg0.equals( "HEAD" ) && !arg0.equals( "BASE" ) );
134 }
135
136 public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter )
137 throws ScmRepositoryException
138 {
139 ScmUrlParserResult result = parseScmUrl( scmSpecificUrl, delimiter );
140
141 if ( result.messages.size() > 0 )
142 {
143 throw new ScmRepositoryException( "The scm url is invalid.", result.messages );
144 }
145
146 return result.repository;
147 }
148
149
150
151
152 public ScmProviderRepository makeProviderScmRepository( File path )
153 throws ScmRepositoryException, UnknownRepositoryStructure
154 {
155 if ( path == null || !path.isDirectory() )
156 {
157 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." );
158 }
159
160 File cvsDirectory = new File( path, "CVS" );
161
162 if ( !cvsDirectory.exists() )
163 {
164 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a cvs checkout directory." );
165 }
166
167 File cvsRootFile = new File( cvsDirectory, "Root" );
168
169 File moduleFile = new File( cvsDirectory, "Repository" );
170
171 String cvsRoot;
172
173 String module;
174
175 try
176 {
177 cvsRoot = FileUtils.fileRead( cvsRootFile ).trim().substring( 1 );
178 }
179 catch ( IOException e )
180 {
181 throw new ScmRepositoryException( "Can't read " + cvsRootFile.getAbsolutePath() );
182 }
183 try
184 {
185 module = FileUtils.fileRead( moduleFile ).trim();
186 }
187 catch ( IOException e )
188 {
189 throw new ScmRepositoryException( "Can't read " + moduleFile.getAbsolutePath() );
190 }
191
192 return makeProviderScmRepository( cvsRoot + ":" + module, ':' );
193 }
194
195 public List validateScmUrl( String scmSpecificUrl, char delimiter )
196 {
197 ScmUrlParserResult result = parseScmUrl( scmSpecificUrl, delimiter );
198
199 return result.messages;
200 }
201
202 public String getScmType()
203 {
204 return "cvs";
205 }
206
207
208
209
210
211 private ScmUrlParserResult parseScmUrl( String scmSpecificUrl, char delimiter )
212 {
213 ScmUrlParserResult result = new ScmUrlParserResult();
214
215 String[] tokens = StringUtils.split( scmSpecificUrl, Character.toString( delimiter ) );
216
217 if ( tokens.length < 3 )
218 {
219 result.messages.add( "The connection string contains too few tokens." );
220
221 return result;
222 }
223
224 String cvsroot;
225
226 String transport = tokens[0];
227
228 if ( transport.equalsIgnoreCase( TRANSPORT_LOCAL ) )
229 {
230
231 cvsroot = tokens[1];
232 }
233 else if ( transport.equalsIgnoreCase( TRANSPORT_PSERVER ) || transport.equalsIgnoreCase( TRANSPORT_LSERVER ) ||
234 transport.equalsIgnoreCase( TRANSPORT_EXT ) || transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
235 {
236 if ( tokens.length != 4 && transport.equalsIgnoreCase( TRANSPORT_EXT ) )
237 {
238 result.messages.add( "The connection string contains too few tokens." );
239
240 return result;
241 }
242 else if ( ( tokens.length < 4 || tokens.length > 6 ) && transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
243 {
244 result.messages.add( "The connection string contains too few tokens." );
245
246 return result;
247 }
248 else if ( tokens.length < 4 || tokens.length > 5 && !transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
249 {
250 result.messages.add( "The connection string contains too few tokens." );
251
252 return result;
253 }
254 else if ( tokens.length != 4 && transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
255 {
256 result.messages.add( "The connection string contains an incorrect number of tokens (should be four)." );
257
258 return result;
259 }
260
261 if ( transport.equalsIgnoreCase( TRANSPORT_LSERVER ) )
262 {
263
264 cvsroot = tokens[1] + ":" + tokens[2];
265 }
266 else
267 {
268
269 if ( tokens.length == 4 )
270 {
271 cvsroot = ":" + transport + ":" + tokens[1] + ":" + tokens[2];
272 }
273 else
274 {
275 cvsroot = ":" + transport + ":" + tokens[1] + ":" + tokens[2] + ":" + tokens[3];
276 }
277 }
278 }
279 else
280 {
281 result.messages.add( "Unknown transport: " + transport );
282
283 return result;
284 }
285
286 String user = null;
287
288 String password = null;
289
290 String host = null;
291
292 String path = null;
293
294 String module = null;
295
296 int port = -1;
297
298 if ( transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
299 {
300
301 port = 2401;
302
303 if ( tokens.length == 4 )
304 {
305
306 String userhost = tokens[1];
307
308 int index = userhost.indexOf( "@" );
309
310 if ( index == -1 )
311 {
312 host = userhost;
313 }
314 else
315 {
316 user = userhost.substring( 0, index );
317
318 host = userhost.substring( index + 1 );
319 }
320
321 path = tokens[2];
322
323 module = tokens[3];
324 }
325 else if ( tokens.length == 6 )
326 {
327
328 user = tokens[1];
329
330 String passhost = tokens[2];
331
332 int index = passhost.indexOf( "@" );
333
334 if ( index == -1 )
335 {
336 result.messages
337 .add( "The user_password_host part must be on the form: <username>:<password>@<hostname>." );
338
339 return result;
340 }
341
342 password = passhost.substring( 0, index );
343
344 host = passhost.substring( index + 1 );
345
346 port = new Integer( tokens[3] ).intValue();
347
348 path = tokens[4];
349
350 module = tokens[5];
351 }
352 else
353 {
354
355 if ( tokens[1].indexOf( "@" ) > 0 )
356 {
357
358 String userhost = tokens[1];
359
360 int index = userhost.indexOf( "@" );
361
362 user = userhost.substring( 0, index );
363
364 host = userhost.substring( index + 1 );
365
366 port = new Integer( tokens[2] ).intValue();
367 }
368 else if ( tokens[2].indexOf( "@" ) >= 0 )
369 {
370
371
372 user = tokens[1];
373
374 String passhost = tokens[2];
375
376 int index = passhost.indexOf( "@" );
377
378 password = passhost.substring( 0, index );
379
380 host = passhost.substring( index + 1 );
381 }
382 else
383 {
384
385 try
386 {
387 port = new Integer( tokens[2] ).intValue();
388 }
389 catch ( Exception e )
390 {
391
392 result.messages.add( "Your scm url is invalid." );
393
394 return result;
395 }
396
397 host = tokens[1];
398 }
399
400 path = tokens[3];
401
402 module = tokens[4];
403 }
404
405 String userHost = host;
406
407 if ( user != null )
408 {
409 userHost = user + "@" + host;
410 }
411
412
413 cvsroot = ":" + transport + ":" + userHost + ":";
414
415 if ( port != -1 )
416 {
417 cvsroot += port;
418 }
419
420 cvsroot += path;
421 }
422 else if ( transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
423 {
424
425 String userhost = tokens[1];
426
427 int index = userhost.indexOf( "@" );
428
429 if ( index == -1 )
430 {
431 user = "";
432
433 host = userhost;
434 }
435 else
436 {
437 user = userhost.substring( 0, index );
438
439 host = userhost.substring( index + 1 );
440 }
441
442 path = tokens[2];
443
444 module = tokens[3];
445
446
447 cvsroot = ":" + transport + ":" + host + ":" + path;
448 }
449 else
450 {
451 if ( !transport.equalsIgnoreCase( TRANSPORT_LOCAL ) )
452 {
453 String userhost = tokens[1];
454
455 int index = userhost.indexOf( "@" );
456
457 if ( index == -1 )
458 {
459 host = userhost;
460 }
461 else
462 {
463 user = userhost.substring( 0, index );
464
465 host = userhost.substring( index + 1 );
466 }
467 }
468
469 if ( transport.equals( TRANSPORT_LOCAL ) )
470 {
471 path = tokens[1];
472
473 module = tokens[2];
474
475 if ( module != null && module.startsWith( "/" ) )
476 {
477 module = module.substring( 1 );
478 }
479
480 }
481 else
482 {
483 if ( tokens.length == 4 )
484 {
485 path = tokens[2];
486
487 module = tokens[3];
488 }
489 else
490 {
491 port = new Integer( tokens[2] ).intValue();
492
493 path = tokens[3];
494
495 module = tokens[4];
496 }
497 }
498 }
499
500 if ( port == -1 )
501 {
502 result.repository = new CvsScmProviderRepository( cvsroot, transport, user, password, host, path, module );
503 }
504 else
505 {
506 result.repository =
507 new CvsScmProviderRepository( cvsroot, transport, user, password, host, port, path, module );
508 }
509
510 return result;
511 }
512
513 private ScmResult executeCommand( Command command, ScmProviderRepository repository, ScmFileSet fileSet,
514 CommandParameters parameters )
515 throws ScmException
516 {
517 fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
518
519 command.setLogger( getLogger() );
520
521 return command.execute( repository, fileSet, parameters );
522 }
523
524 protected abstract Command getAddCommand();
525
526 protected abstract Command getBranchCommand();
527
528 protected abstract Command getChangeLogCommand();
529
530 protected abstract Command getCheckInCommand();
531
532 protected abstract Command getCheckOutCommand();
533
534 protected abstract Command getDiffCommand();
535
536 protected abstract Command getExportCommand();
537
538 protected abstract Command getListCommand();
539
540 protected abstract Command getLoginCommand();
541
542 protected abstract Command getRemoveCommand();
543
544 protected abstract Command getStatusCommand();
545
546 protected abstract Command getTagCommand();
547
548 protected abstract Command getUpdateCommand();
549
550
551
552
553 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
554 throws ScmException
555 {
556 return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters );
557 }
558
559
560
561
562 public BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
563 throws ScmException
564 {
565 return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters );
566 }
567
568
569
570
571 public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
572 CommandParameters parameters )
573 throws ScmException
574 {
575 return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters );
576 }
577
578
579
580
581 public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
582 CommandParameters parameters )
583 throws ScmException
584 {
585 return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters );
586 }
587
588
589
590
591 public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
592 CommandParameters parameters )
593 throws ScmException
594 {
595 return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters );
596 }
597
598
599
600
601 public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
602 throws ScmException
603 {
604 return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters );
605 }
606
607
608
609
610 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet,
611 CommandParameters parameters )
612 throws ScmException
613 {
614 return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters );
615 }
616
617
618
619
620 public LoginScmResult login( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
621 throws ScmException
622 {
623 return (LoginScmResult) executeCommand( getLoginCommand(), repository, fileSet, parameters );
624 }
625
626
627
628
629 public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
630 throws ScmException
631 {
632 return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters );
633 }
634
635
636
637
638 public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
639 throws ScmException
640 {
641 return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters );
642 }
643
644
645
646
647 public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
648 throws ScmException
649 {
650 return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters );
651 }
652
653
654
655
656 public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
657 throws ScmException
658 {
659 return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters );
660 }
661
662
663
664
665 protected ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
666 throws ScmException
667 {
668 return (ListScmResult) executeCommand( getListCommand(), repository, fileSet, parameters );
669 }
670
671
672
673
674
675
676
677
678
679 private static ScmFileSet fixUpScmFileSetAbsoluteFilePath( ScmFileSet currentFileSet )
680 throws ScmException
681 {
682 ScmFileSet newFileSet = null;
683
684 try
685 {
686 File basedir = getAbsoluteFilePath( currentFileSet.getBasedir() );
687
688 File[] files = currentFileSet.getFiles();
689
690 for ( int i = 0; i < files.length; ++i )
691 {
692 if ( files[i].isAbsolute() )
693 {
694 files[i] = new File( getRelativePath( basedir, files[i] ) );
695 }
696 }
697
698 newFileSet = new ScmFileSet( basedir, files );
699 }
700 catch ( IOException e )
701 {
702 throw new ScmException( "Invalid file set.", e );
703 }
704
705 return newFileSet;
706 }
707
708 public static String getRelativePath( File basedir, File f )
709 throws ScmException, IOException
710 {
711 File fileOrDir = getAbsoluteFilePath( f );
712
713 if ( !fileOrDir.getPath().startsWith( basedir.getPath() ) )
714 {
715 throw new ScmException( fileOrDir.getPath() + " was not contained in " + basedir.getPath() );
716 }
717
718 return fileOrDir.getPath().substring( basedir.getPath().length() + 1, fileOrDir.getPath().length() );
719 }
720
721 private static File getAbsoluteFilePath( File fileOrDir )
722 throws IOException
723 {
724 String javaPathString = fileOrDir.getCanonicalPath().replace( '\\', '/' );
725
726 if ( javaPathString.endsWith( "/" ) )
727 {
728 javaPathString = javaPathString.substring( 0, javaPathString.length() - 1 );
729 }
730
731 return new File( javaPathString );
732 }
733 }