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 )
203 {
204 throw new NullPointerException( "Path argument is null" );
205 }
206
207 if ( !path.isDirectory() )
208 {
209 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." );
210 }
211
212 File cvsDirectory = new File( path, "CVS" );
213
214 if ( !cvsDirectory.exists() )
215 {
216 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a cvs checkout directory." );
217 }
218
219 File cvsRootFile = new File( cvsDirectory, "Root" );
220
221 File moduleFile = new File( cvsDirectory, "Repository" );
222
223 String cvsRoot;
224
225 String module;
226
227 try
228 {
229 cvsRoot = FileUtils.fileRead( cvsRootFile ).trim().substring( 1 );
230 }
231 catch ( IOException e )
232 {
233 throw new ScmRepositoryException( "Can't read " + cvsRootFile.getAbsolutePath() );
234 }
235 try
236 {
237 module = FileUtils.fileRead( moduleFile ).trim();
238 }
239 catch ( IOException e )
240 {
241 throw new ScmRepositoryException( "Can't read " + moduleFile.getAbsolutePath() );
242 }
243
244 return makeProviderScmRepository( cvsRoot + ":" + module, ':' );
245 }
246
247
248 public List<String> validateScmUrl( String scmSpecificUrl, char delimiter )
249 {
250 ScmUrlParserResult result = parseScmUrl( scmSpecificUrl, delimiter );
251
252 return result.getMessages();
253 }
254
255
256 public String getScmType()
257 {
258 return "cvs";
259 }
260
261
262 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
263 throws ScmException
264 {
265 return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters );
266 }
267
268
269 public BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
270 throws ScmException
271 {
272 return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters );
273 }
274
275
276 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
277 throws ScmException
278 {
279 return (BlameScmResult) executeCommand( getBlameCommand(), repository, fileSet, parameters );
280 }
281
282
283 public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
284 CommandParameters parameters )
285 throws ScmException
286 {
287 return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters );
288 }
289
290
291 public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
292 CommandParameters parameters )
293 throws ScmException
294 {
295 return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters );
296 }
297
298
299 public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
300 CommandParameters parameters )
301 throws ScmException
302 {
303 return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters );
304 }
305
306
307 public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
308 throws ScmException
309 {
310 return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters );
311 }
312
313
314 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet,
315 CommandParameters parameters )
316 throws ScmException
317 {
318 return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters );
319 }
320
321
322 public LoginScmResult login( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
323 throws ScmException
324 {
325 return (LoginScmResult) executeCommand( getLoginCommand(), repository, fileSet, parameters );
326 }
327
328
329 public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
330 throws ScmException
331 {
332 return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters );
333 }
334
335
336 public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
337 throws ScmException
338 {
339 return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters );
340 }
341
342
343 public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
344 throws ScmException
345 {
346 return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters );
347 }
348
349 protected TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters,
350 ScmTagParameters scmParameters )
351 throws ScmException
352 {
353 return (TagScmResult) getTagCommand().execute( repository, fileSet, parameters );
354 }
355
356
357
358 public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
359 throws ScmException
360 {
361 return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters );
362 }
363
364
365 protected ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
366 throws ScmException
367 {
368 return (ListScmResult) executeCommand( getListCommand(), repository, fileSet, parameters );
369 }
370
371
372 protected MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
373 throws ScmException
374 {
375 return (MkdirScmResult) executeCommand( getMkdirCommand(), repository, fileSet, parameters );
376 }
377
378
379
380
381
382
383
384
385 public static String getRelativePath( File basedir, File f )
386 throws ScmException, IOException
387 {
388 File fileOrDir = getAbsoluteFilePath( f );
389
390 if ( !fileOrDir.getPath().startsWith( basedir.getPath() ) )
391 {
392 throw new ScmException( fileOrDir.getPath() + " was not contained in " + basedir.getPath() );
393 }
394
395 return fileOrDir.getPath().substring( basedir.getPath().length() + 1, fileOrDir.getPath().length() );
396 }
397
398
399
400
401
402 protected ScmUrlParserResult parseScmUrl( String scmSpecificUrl, char delimiter )
403 {
404 ScmUrlParserResult result = new ScmUrlParserResult();
405
406 String[] tokens = StringUtils.split( scmSpecificUrl, Character.toString( delimiter ) );
407
408 if ( tokens.length < 3 )
409 {
410 result.getMessages().add( "The connection string contains too few tokens." );
411
412 return result;
413 }
414
415 String cvsroot;
416
417 String transport = tokens[0];
418
419 if ( transport.equalsIgnoreCase( TRANSPORT_LOCAL ) )
420 {
421
422 cvsroot = tokens[1];
423 }
424 else if ( transport.equalsIgnoreCase( TRANSPORT_PSERVER ) || transport.equalsIgnoreCase( TRANSPORT_LSERVER )
425 || transport.equalsIgnoreCase( TRANSPORT_EXT ) || transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
426 {
427 if ( tokens.length != 4 && transport.equalsIgnoreCase( TRANSPORT_EXT ) )
428 {
429 result.getMessages().add( "The connection string contains too few tokens." );
430
431 return result;
432 }
433 else if ( ( tokens.length < 4 || tokens.length > 6 ) && transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
434 {
435 result.getMessages().add( "The connection string contains too few tokens." );
436
437 return result;
438 }
439 else if ( tokens.length < 4 || tokens.length > 5 && !transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
440 {
441 result.getMessages().add( "The connection string contains too few tokens." );
442
443 return result;
444 }
445 else if ( tokens.length < 4 || tokens.length > 5 && transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
446 {
447 result.getMessages().add( "The connection string contains too few tokens." );
448
449 return result;
450 }
451
452 if ( transport.equalsIgnoreCase( TRANSPORT_LSERVER ) )
453 {
454
455 cvsroot = tokens[1] + ":" + tokens[2];
456 }
457 else
458 {
459
460 if ( tokens.length == 4 )
461 {
462 cvsroot = ":" + transport + ":" + tokens[1] + ":" + tokens[2];
463 }
464 else
465 {
466 cvsroot = ":" + transport + ":" + tokens[1] + ":" + tokens[2] + ":" + tokens[3];
467 }
468 }
469 }
470 else
471 {
472 result.getMessages().add( "Unknown transport: " + transport );
473
474 return result;
475 }
476
477 String user = null;
478
479 String password = null;
480
481 String host = null;
482
483 String path = null;
484
485 String module = null;
486
487 int port = -1;
488
489 if ( transport.equalsIgnoreCase( TRANSPORT_PSERVER ) )
490 {
491
492 port = 2401;
493
494 if ( tokens.length == 4 )
495 {
496
497 String userhost = tokens[1];
498
499 int index = userhost.indexOf( '@' );
500
501 if ( index == -1 )
502 {
503 host = userhost;
504 }
505 else
506 {
507 user = userhost.substring( 0, index );
508
509 host = userhost.substring( index + 1 );
510 }
511
512 path = tokens[2];
513
514 module = tokens[3];
515 }
516 else if ( tokens.length == 6 )
517 {
518
519 user = tokens[1];
520
521 String passhost = tokens[2];
522
523 int index = passhost.indexOf( '@' );
524
525 if ( index == -1 )
526 {
527 result.getMessages()
528 .add( "The user_password_host part must be on the form: <username>:<password>@<hostname>." );
529
530 return result;
531 }
532
533 password = passhost.substring( 0, index );
534
535 host = passhost.substring( index + 1 );
536
537 port = Integer.valueOf( tokens[3] ).intValue();
538
539 path = tokens[4];
540
541 module = tokens[5];
542 }
543 else
544 {
545
546 if ( tokens[1].indexOf( '@' ) > 0 )
547 {
548
549 String userhost = tokens[1];
550
551 int index = userhost.indexOf( '@' );
552
553 user = userhost.substring( 0, index );
554
555 host = userhost.substring( index + 1 );
556
557 port = new Integer( tokens[2] ).intValue();
558 }
559 else if ( tokens[2].indexOf( '@' ) >= 0 )
560 {
561
562
563 user = tokens[1];
564
565 String passhost = tokens[2];
566
567 int index = passhost.indexOf( '@' );
568
569 password = passhost.substring( 0, index );
570
571 host = passhost.substring( index + 1 );
572 }
573 else
574 {
575
576 try
577 {
578 port = new Integer( tokens[2] ).intValue();
579 }
580 catch ( Exception e )
581 {
582
583 result.getMessages().add( "Your scm url is invalid." );
584
585 return result;
586 }
587
588 host = tokens[1];
589 }
590
591 path = tokens[3];
592
593 module = tokens[4];
594 }
595
596 String userHost = host;
597
598 if ( user != null )
599 {
600 userHost = user + "@" + host;
601 }
602
603
604 cvsroot = ":" + transport + ":" + userHost + ":";
605
606 if ( port != -1 )
607 {
608 cvsroot += port;
609 }
610
611 cvsroot += path;
612 }
613 else if ( transport.equalsIgnoreCase( TRANSPORT_SSPI ) )
614 {
615
616 String userhost = tokens[1];
617
618 int index = userhost.indexOf( '@' );
619
620 if ( index == -1 )
621 {
622 user = "";
623
624 host = userhost;
625 }
626 else
627 {
628 user = userhost.substring( 0, index );
629
630 host = userhost.substring( index + 1 );
631 }
632
633
634 if ( tokens.length == 4 )
635 {
636 path = tokens[2];
637 module = tokens[3];
638 }
639 else
640 {
641
642 try
643 {
644 port = new Integer( tokens[2] ).intValue();
645 path = tokens[3];
646 module = tokens[4];
647 }
648 catch ( Exception e )
649 {
650
651 result.getMessages().add( "Your scm url is invalid, could not get port value." );
652
653 return result;
654 }
655 }
656
657
658 cvsroot = ":" + transport + ":" + host + ":";
659
660 if ( port != -1 )
661 {
662 cvsroot += port;
663 }
664
665 cvsroot += path;
666 }
667 else
668 {
669 if ( !transport.equalsIgnoreCase( TRANSPORT_LOCAL ) )
670 {
671 String userhost = tokens[1];
672
673 int index = userhost.indexOf( '@' );
674
675 if ( index == -1 )
676 {
677 host = userhost;
678 }
679 else
680 {
681 user = userhost.substring( 0, index );
682
683 host = userhost.substring( index + 1 );
684 }
685 }
686
687 if ( transport.equals( TRANSPORT_LOCAL ) )
688 {
689 path = tokens[1];
690
691 module = tokens[2];
692
693 if ( module != null && module.startsWith( "/" ) )
694 {
695 module = module.substring( 1 );
696 }
697
698 }
699 else
700 {
701 if ( tokens.length == 4 )
702 {
703 path = tokens[2];
704
705 module = tokens[3];
706 }
707 else
708 {
709 port = new Integer( tokens[2] ).intValue();
710
711 path = tokens[3];
712
713 module = tokens[4];
714 }
715 }
716 }
717
718 if ( port == -1 )
719 {
720 result.setRepository( new CvsScmProviderRepository( cvsroot, transport, user, password, host, path,
721 module ) );
722 }
723 else
724 {
725 result.setRepository( new CvsScmProviderRepository( cvsroot, transport, user, password, host, port,
726 path, module ) );
727 }
728
729 return result;
730 }
731
732 protected abstract Command getAddCommand();
733
734 protected abstract Command getBranchCommand();
735
736 protected abstract Command getBlameCommand();
737
738 protected abstract Command getChangeLogCommand();
739
740 protected abstract Command getCheckInCommand();
741
742 protected abstract Command getCheckOutCommand();
743
744 protected abstract Command getDiffCommand();
745
746 protected abstract Command getExportCommand();
747
748 protected abstract Command getListCommand();
749
750 protected abstract Command getLoginCommand();
751
752 protected abstract Command getRemoveCommand();
753
754 protected abstract Command getStatusCommand();
755
756 protected abstract Command getTagCommand();
757
758 protected abstract Command getUpdateCommand();
759
760 protected abstract Command getMkdirCommand();
761
762
763
764
765
766 private ScmResult executeCommand( Command command, ScmProviderRepository repository, ScmFileSet fileSet,
767 CommandParameters parameters )
768 throws ScmException
769 {
770 fileSet = fixUpScmFileSetAbsoluteFilePath( fileSet );
771
772 command.setLogger( getLogger() );
773
774 return command.execute( repository, fileSet, parameters );
775 }
776
777
778
779
780
781
782
783
784
785
786 private static ScmFileSet fixUpScmFileSetAbsoluteFilePath( ScmFileSet currentFileSet )
787 throws ScmException
788 {
789 ScmFileSet newFileSet = null;
790
791 try
792 {
793 File basedir = getAbsoluteFilePath( currentFileSet.getBasedir() );
794 List<File> fixedFiles = new ArrayList<File>( currentFileSet.getFileList().size() );
795 for ( File file : currentFileSet.getFileList() )
796 {
797 if ( file.isAbsolute() )
798 {
799 fixedFiles.add( new File( getRelativePath( basedir, file ) ) );
800 }
801 else
802 {
803 fixedFiles.add( file );
804 }
805 }
806
807 newFileSet = new ScmFileSet( basedir, fixedFiles );
808 }
809 catch ( IOException e )
810 {
811 throw new ScmException( "Invalid file set.", e );
812 }
813
814 return newFileSet;
815 }
816
817 private static File getAbsoluteFilePath( File fileOrDir )
818 throws IOException
819 {
820 String javaPathString = fileOrDir.getCanonicalPath().replace( '\\', '/' );
821
822 if ( javaPathString.endsWith( "/" ) )
823 {
824 javaPathString = javaPathString.substring( 0, javaPathString.length() - 1 );
825 }
826
827 return new File( javaPathString );
828 }
829 }