1 package org.apache.maven.wagon.providers.ftp;
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.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.util.ArrayList;
28 import java.util.Calendar;
29 import java.util.List;
30
31 import org.apache.commons.net.ProtocolCommandEvent;
32 import org.apache.commons.net.ProtocolCommandListener;
33 import org.apache.commons.net.ftp.FTP;
34 import org.apache.commons.net.ftp.FTPClient;
35 import org.apache.commons.net.ftp.FTPFile;
36 import org.apache.commons.net.ftp.FTPReply;
37 import org.apache.maven.wagon.ConnectionException;
38 import org.apache.maven.wagon.InputData;
39 import org.apache.maven.wagon.OutputData;
40 import org.apache.maven.wagon.PathUtils;
41 import org.apache.maven.wagon.ResourceDoesNotExistException;
42 import org.apache.maven.wagon.StreamWagon;
43 import org.apache.maven.wagon.TransferFailedException;
44 import org.apache.maven.wagon.WagonConstants;
45 import org.apache.maven.wagon.authentication.AuthenticationException;
46 import org.apache.maven.wagon.authentication.AuthenticationInfo;
47 import org.apache.maven.wagon.authorization.AuthorizationException;
48 import org.apache.maven.wagon.repository.RepositoryPermissions;
49 import org.apache.maven.wagon.resource.Resource;
50 import org.codehaus.plexus.util.IOUtil;
51
52
53
54
55
56
57
58
59
60
61 public class FtpWagon
62 extends StreamWagon
63 {
64 private FTPClient ftp;
65
66
67 private boolean passiveMode = true;
68
69 public boolean isPassiveMode()
70 {
71 return passiveMode;
72 }
73
74 public void setPassiveMode( boolean passiveMode )
75 {
76 this.passiveMode = passiveMode;
77 }
78
79 protected void openConnectionInternal()
80 throws ConnectionException, AuthenticationException
81 {
82 AuthenticationInfo authInfo = getAuthenticationInfo();
83
84 if ( authInfo == null )
85 {
86 throw new IllegalArgumentException( "Authentication Credentials cannot be null for FTP protocol" );
87 }
88
89 if ( authInfo.getUserName() == null )
90 {
91 authInfo.setUserName( System.getProperty( "user.name" ) );
92 }
93
94 String username = authInfo.getUserName();
95
96 String password = authInfo.getPassword();
97
98 if ( username == null )
99 {
100 throw new AuthenticationException( "Username not specified for repository " + getRepository().getId() );
101 }
102 if ( password == null )
103 {
104 throw new AuthenticationException( "Password not specified for repository " + getRepository().getId() );
105 }
106
107 String host = getRepository().getHost();
108
109 ftp = new FTPClient();
110 ftp.setDefaultTimeout( getTimeout() );
111 ftp.setDataTimeout( getTimeout() );
112
113 ftp.addProtocolCommandListener( new PrintCommandListener( this ) );
114
115 try
116 {
117 if ( getRepository().getPort() != WagonConstants.UNKNOWN_PORT )
118 {
119 ftp.connect( host, getRepository().getPort() );
120 }
121 else
122 {
123 ftp.connect( host );
124 }
125
126
127
128
129 int reply = ftp.getReplyCode();
130
131 if ( !FTPReply.isPositiveCompletion( reply ) )
132 {
133 ftp.disconnect();
134
135 throw new AuthenticationException( "FTP server refused connection." );
136 }
137 }
138 catch ( IOException e )
139 {
140 if ( ftp.isConnected() )
141 {
142 try
143 {
144 fireSessionError( e );
145
146 ftp.disconnect();
147 }
148 catch ( IOException f )
149 {
150
151 }
152 }
153
154 throw new AuthenticationException( "Could not connect to server.", e );
155 }
156
157 try
158 {
159 if ( !ftp.login( username, password ) )
160 {
161 throw new AuthenticationException( "Cannot login to remote system" );
162 }
163
164 fireSessionDebug( "Remote system is " + ftp.getSystemName() );
165
166
167 ftp.setFileType( FTP.BINARY_FILE_TYPE );
168 ftp.setListHiddenFiles( true );
169
170
171
172 if ( isPassiveMode() )
173 {
174 ftp.enterLocalPassiveMode();
175 }
176 }
177 catch ( IOException e )
178 {
179 throw new ConnectionException( "Cannot login to remote system", e );
180 }
181 }
182
183 protected void firePutCompleted( Resource resource, File file )
184 {
185 try
186 {
187
188 ftp.completePendingCommand();
189
190 RepositoryPermissions permissions = repository.getPermissions();
191
192 if ( permissions != null && permissions.getGroup() != null )
193 {
194
195 ftp.sendSiteCommand( "CHGRP " + permissions.getGroup() + " " + resource.getName() );
196 }
197
198 if ( permissions != null && permissions.getFileMode() != null )
199 {
200
201 ftp.sendSiteCommand( "CHMOD " + permissions.getFileMode() + " " + resource.getName() );
202 }
203 }
204 catch ( IOException e )
205 {
206
207
208
209 }
210
211 super.firePutCompleted( resource, file );
212 }
213
214 protected void fireGetCompleted( Resource resource, File localFile )
215 {
216 try
217 {
218 ftp.completePendingCommand();
219 }
220 catch ( IOException e )
221 {
222
223
224
225
226 }
227 super.fireGetCompleted( resource, localFile );
228 }
229
230 public void closeConnection()
231 throws ConnectionException
232 {
233 if ( ftp != null && ftp.isConnected() )
234 {
235 try
236 {
237
238 ftp.disconnect();
239 }
240 catch ( IOException e )
241 {
242 throw new ConnectionException( "Failed to close connection to FTP repository", e );
243 }
244 }
245 }
246
247 public void fillOutputData( OutputData outputData )
248 throws TransferFailedException
249 {
250 OutputStream os;
251
252 Resource resource = outputData.getResource();
253
254 RepositoryPermissions permissions = repository.getPermissions();
255
256 try
257 {
258 if ( !ftp.changeWorkingDirectory( getRepository().getBasedir() ) )
259 {
260 throw new TransferFailedException(
261 "Required directory: '" + getRepository().getBasedir() + "' " + "is missing" );
262 }
263
264 String[] dirs = PathUtils.dirnames( resource.getName() );
265
266 for ( int i = 0; i < dirs.length; i++ )
267 {
268 boolean dirChanged = ftp.changeWorkingDirectory( dirs[i] );
269
270 if ( !dirChanged )
271 {
272
273 boolean success = ftp.makeDirectory( dirs[i] );
274
275 if ( success )
276 {
277 if ( permissions != null && permissions.getGroup() != null )
278 {
279
280 ftp.sendSiteCommand( "CHGRP " + permissions.getGroup() + " " + dirs[i] );
281 }
282
283 if ( permissions != null && permissions.getDirectoryMode() != null )
284 {
285
286 ftp.sendSiteCommand( "CHMOD " + permissions.getDirectoryMode() + " " + dirs[i] );
287 }
288
289 dirChanged = ftp.changeWorkingDirectory( dirs[i] );
290 }
291 }
292
293 if ( !dirChanged )
294 {
295 throw new TransferFailedException( "Unable to create directory " + dirs[i] );
296 }
297 }
298
299
300
301 if ( !ftp.changeWorkingDirectory( getRepository().getBasedir() ) )
302 {
303 throw new TransferFailedException( "Unable to return to the base directory" );
304 }
305
306 os = ftp.storeFileStream( resource.getName() );
307
308 if ( os == null )
309 {
310 String msg = "Cannot transfer resource: '" + resource
311 + "'. Output stream is null. FTP Server response: " + ftp.getReplyString();
312
313 throw new TransferFailedException( msg );
314
315 }
316
317 fireTransferDebug( "resource = " + resource );
318
319 }
320 catch ( IOException e )
321 {
322 throw new TransferFailedException( "Error transferring over FTP", e );
323 }
324
325 outputData.setOutputStream( os );
326
327 }
328
329
330
331
332
333 public void fillInputData( InputData inputData )
334 throws TransferFailedException, ResourceDoesNotExistException
335 {
336 InputStream is;
337
338 Resource resource = inputData.getResource();
339
340 try
341 {
342 ftpChangeDirectory( resource );
343
344 String filename = PathUtils.filename( resource.getName() );
345 FTPFile[] ftpFiles = ftp.listFiles( filename );
346
347 if ( ftpFiles == null || ftpFiles.length <= 0 )
348 {
349 throw new ResourceDoesNotExistException( "Could not find file: '" + resource + "'" );
350 }
351
352 long contentLength = ftpFiles[0].getSize();
353
354
355
356
357 Calendar timestamp = ftpFiles[0].getTimestamp();
358 long lastModified = timestamp != null ? timestamp.getTimeInMillis() : 0;
359
360 resource.setContentLength( contentLength );
361
362 resource.setLastModified( lastModified );
363
364 is = ftp.retrieveFileStream( filename );
365 }
366 catch ( IOException e )
367 {
368 throw new TransferFailedException( "Error transferring file via FTP", e );
369 }
370
371 inputData.setInputStream( is );
372 }
373
374 private void ftpChangeDirectory( Resource resource )
375 throws IOException, TransferFailedException, ResourceDoesNotExistException
376 {
377 if ( !ftp.changeWorkingDirectory( getRepository().getBasedir() ) )
378 {
379 throw new ResourceDoesNotExistException(
380 "Required directory: '" + getRepository().getBasedir() + "' " + "is missing" );
381 }
382
383 String[] dirs = PathUtils.dirnames( resource.getName() );
384
385 for ( int i = 0; i < dirs.length; i++ )
386 {
387 boolean dirChanged = ftp.changeWorkingDirectory( dirs[i] );
388
389 if ( !dirChanged )
390 {
391 String msg = "Resource " + resource + " not found. Directory " + dirs[i] + " does not exist";
392
393 throw new ResourceDoesNotExistException( msg );
394 }
395 }
396 }
397
398 public class PrintCommandListener
399 implements ProtocolCommandListener
400 {
401 private FtpWagon wagon;
402
403 public PrintCommandListener( FtpWagon wagon )
404 {
405 this.wagon = wagon;
406 }
407
408 public void protocolCommandSent( ProtocolCommandEvent event )
409 {
410 wagon.fireSessionDebug( "Command sent: " + event.getMessage() );
411
412 }
413
414 public void protocolReplyReceived( ProtocolCommandEvent event )
415 {
416 wagon.fireSessionDebug( "Reply received: " + event.getMessage() );
417 }
418 }
419
420 protected void fireSessionDebug( String msg )
421 {
422 super.fireSessionDebug( msg );
423 }
424
425 public List getFileList( String destinationDirectory )
426 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
427 {
428 Resource resource = new Resource( destinationDirectory );
429
430 try
431 {
432 ftpChangeDirectory( resource );
433
434 String filename = PathUtils.filename( resource.getName() );
435 FTPFile[] ftpFiles = ftp.listFiles( filename );
436
437 if ( ftpFiles == null || ftpFiles.length <= 0 )
438 {
439 throw new ResourceDoesNotExistException( "Could not find file: '" + resource + "'" );
440 }
441
442 List ret = new ArrayList();
443 for( int i=0; i < ftpFiles.length; i++ )
444 {
445 String name = ftpFiles[i].getName();
446
447 if ( ftpFiles[i].isDirectory() && !name.endsWith( "/" ) )
448 {
449 name += "/";
450 }
451
452 ret.add( name );
453 }
454
455 return ret;
456 }
457 catch ( IOException e )
458 {
459 throw new TransferFailedException( "Error transferring file via FTP", e );
460 }
461 }
462
463 public boolean resourceExists( String resourceName )
464 throws TransferFailedException, AuthorizationException
465 {
466 Resource resource = new Resource( resourceName );
467
468 try
469 {
470 ftpChangeDirectory( resource );
471
472 String filename = PathUtils.filename( resource.getName() );
473 int status = ftp.stat( filename );
474
475 return ( ( status == FTPReply.FILE_STATUS ) || ( status == FTPReply.FILE_STATUS_OK )
476 || ( status == FTPReply.SYSTEM_STATUS ) );
477 }
478 catch ( IOException e )
479 {
480 throw new TransferFailedException( "Error transferring file via FTP", e );
481 }
482 catch ( ResourceDoesNotExistException e )
483 {
484 return false;
485 }
486 }
487
488 public boolean supportsDirectoryCopy()
489 {
490 return true;
491 }
492
493 public void putDirectory( File sourceDirectory, String destinationDirectory )
494 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
495 {
496
497
498 try
499 {
500 if ( !ftp.changeWorkingDirectory( getRepository().getBasedir() ) )
501 {
502 throw new TransferFailedException( "Required directory: '" + getRepository().getBasedir() + "' "
503 + "is missing" );
504 }
505 }
506 catch ( IOException e )
507 {
508 throw new TransferFailedException( "Cannot change to root path " + getRepository().getBasedir() );
509 }
510
511 fireTransferDebug( "Recursively uploading directory " + sourceDirectory.getAbsolutePath() + " as "
512 + destinationDirectory );
513 ftpRecursivePut( sourceDirectory, destinationDirectory );
514 }
515
516 private void ftpRecursivePut( File sourceFile, String fileName ) throws TransferFailedException
517 {
518 final RepositoryPermissions permissions = repository.getPermissions();
519
520 fireTransferDebug( "processing = " + sourceFile.getAbsolutePath() + " as " + fileName );
521
522 if ( sourceFile.isDirectory() )
523 {
524 if ( !fileName.equals( "." ) )
525 {
526 try
527 {
528
529 if ( !ftp.changeWorkingDirectory( fileName ) )
530 {
531
532 if ( ftp.makeDirectory( fileName ) )
533 {
534 if ( permissions != null )
535 {
536
537
538 String group = permissions.getGroup();
539 if ( group != null )
540 {
541 try
542 {
543 ftp.sendSiteCommand( "CHGRP " + permissions.getGroup() );
544 }
545 catch ( IOException e )
546 {
547 }
548 }
549 String mode = permissions.getDirectoryMode();
550 if ( mode != null )
551 {
552 try
553 {
554 ftp.sendSiteCommand( "CHMOD " + permissions.getDirectoryMode() );
555 }
556 catch ( IOException e )
557 {
558 }
559 }
560 }
561
562 if ( !ftp.changeWorkingDirectory( fileName ) )
563 {
564 throw new TransferFailedException( "Unable to change cwd on ftp server to " + fileName
565 + " when processing " + sourceFile.getAbsolutePath() );
566 }
567 }
568 else
569 {
570 throw new TransferFailedException( "Unable to create directory " + fileName
571 + " when processing " + sourceFile.getAbsolutePath() );
572 }
573 }
574 }
575 catch ( IOException e )
576 {
577 throw new TransferFailedException( "IOException caught while processing path at "
578 + sourceFile.getAbsolutePath(), e );
579 }
580 }
581
582 File[] files = sourceFile.listFiles();
583 if ( files != null && files.length > 0 )
584 {
585 fireTransferDebug( "listing children of = " + sourceFile.getAbsolutePath() + " found " + files.length );
586
587
588 for ( int i = 0; i < files.length; i++ )
589 {
590 if ( files[i].isDirectory() )
591 {
592 ftpRecursivePut( files[i], files[i].getName() );
593 }
594 }
595 for ( int i = 0; i < files.length; i++ )
596 {
597 if ( !files[i].isDirectory() )
598 {
599 ftpRecursivePut( files[i], files[i].getName() );
600 }
601 }
602 }
603
604
605 try
606 {
607 ftp.changeToParentDirectory();
608 }
609 catch ( IOException e )
610 {
611 throw new TransferFailedException( "IOException caught while attempting to step up to parent directory"
612 + " after successfully processing " + sourceFile.getAbsolutePath(),
613 e );
614 }
615 }
616 else
617 {
618
619
620 FileInputStream sourceFileStream = null;
621 try
622 {
623 sourceFileStream = new FileInputStream( sourceFile );
624
625
626 if ( ftp.storeFile( fileName, sourceFileStream ) )
627 {
628 if ( permissions != null )
629 {
630
631
632 String group = permissions.getGroup();
633 if ( group != null )
634 try
635 {
636 ftp.sendSiteCommand( "CHGRP " + permissions.getGroup() );
637 }
638 catch ( IOException e )
639 {
640 }
641 String mode = permissions.getFileMode();
642 if ( mode != null )
643 try
644 {
645 ftp.sendSiteCommand( "CHMOD " + permissions.getDirectoryMode() );
646 }
647 catch ( IOException e )
648 {
649 }
650 }
651 }
652 else
653 {
654 String msg =
655 "Cannot transfer resource: '" + sourceFile.getAbsolutePath() + "' FTP Server response: "
656 + ftp.getReplyString();
657 throw new TransferFailedException( msg );
658 }
659 }
660 catch ( IOException e )
661 {
662 throw new TransferFailedException( "IOException caught while attempting to upload "
663 + sourceFile.getAbsolutePath(), e );
664 }
665 finally
666 {
667 IOUtil.close( sourceFileStream );
668 }
669
670 }
671
672 fireTransferDebug( "completed = " + sourceFile.getAbsolutePath() );
673 }
674 }