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