1 package org.apache.maven.wagon.providers.ssh.jsch;
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.InputStream;
24 import java.io.OutputStream;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Vector;
29
30 import org.apache.maven.wagon.InputData;
31 import org.apache.maven.wagon.OutputData;
32 import org.apache.maven.wagon.PathUtils;
33 import org.apache.maven.wagon.ResourceDoesNotExistException;
34 import org.apache.maven.wagon.TransferFailedException;
35 import org.apache.maven.wagon.authentication.AuthenticationException;
36 import org.apache.maven.wagon.authorization.AuthorizationException;
37 import org.apache.maven.wagon.events.TransferEvent;
38 import org.apache.maven.wagon.providers.ssh.ScpHelper;
39 import org.apache.maven.wagon.repository.RepositoryPermissions;
40 import org.apache.maven.wagon.resource.Resource;
41
42 import com.jcraft.jsch.ChannelSftp;
43 import com.jcraft.jsch.JSchException;
44 import com.jcraft.jsch.SftpATTRS;
45 import com.jcraft.jsch.SftpException;
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class SftpWagon
60 extends AbstractJschWagon
61 {
62 private static final String SFTP_CHANNEL = "sftp";
63
64 private static final int S_IFDIR = 0x4000;
65
66 private static final long MILLIS_PER_SEC = 1000L;
67
68 private ChannelSftp channel;
69
70 public void closeConnection()
71 {
72 if ( channel != null )
73 {
74 channel.disconnect();
75 }
76 super.closeConnection();
77 }
78
79 public void openConnectionInternal()
80 throws AuthenticationException
81 {
82 super.openConnectionInternal();
83
84 try
85 {
86 channel = (ChannelSftp) session.openChannel( SFTP_CHANNEL );
87
88 channel.connect();
89 }
90 catch ( JSchException e )
91 {
92 throw new AuthenticationException( "Error connecting to remote repository: " + getRepository().getUrl(),
93 e );
94 }
95 }
96
97 private void returnToParentDirectory( Resource resource )
98 {
99 try
100 {
101 String dir = ScpHelper.getResourceDirectory( resource.getName() );
102 String[] dirs = PathUtils.dirnames( dir );
103 for ( int i = 0; i < dirs.length; i++ )
104 {
105 channel.cd( ".." );
106 }
107 }
108 catch ( SftpException e )
109 {
110 fireTransferDebug( "Error returning to parent directory: " + e.getMessage() );
111 }
112 }
113
114 private void putFile( File source, Resource resource, RepositoryPermissions permissions )
115 throws SftpException, TransferFailedException
116 {
117 resource.setContentLength( source.length() );
118
119 resource.setLastModified( source.lastModified() );
120
121 String filename = ScpHelper.getResourceFilename( resource.getName() );
122
123 firePutStarted( resource, source );
124
125 channel.put( source.getAbsolutePath(), filename );
126
127 postProcessListeners( resource, source, TransferEvent.REQUEST_PUT );
128
129 if ( permissions != null && permissions.getGroup() != null )
130 {
131 setGroup( filename, permissions );
132 }
133
134 if ( permissions != null && permissions.getFileMode() != null )
135 {
136 setFileMode( filename, permissions );
137 }
138
139 firePutCompleted( resource, source );
140 }
141
142 private void setGroup( String filename, RepositoryPermissions permissions )
143 {
144 try
145 {
146 int group = Integer.valueOf( permissions.getGroup() ).intValue();
147 channel.chgrp( group, filename );
148 }
149 catch ( NumberFormatException e )
150 {
151
152 fireTransferDebug( "Not setting group: must be a numerical GID for SFTP" );
153 }
154 catch ( SftpException e )
155 {
156 fireTransferDebug( "Not setting group: " + e.getMessage() );
157 }
158 }
159
160 private void setFileMode( String filename, RepositoryPermissions permissions )
161 {
162 try
163 {
164 int mode = getOctalMode( permissions.getFileMode() );
165 channel.chmod( mode, filename );
166 }
167 catch ( NumberFormatException e )
168 {
169
170 fireTransferDebug( "Not setting mode: must be a numerical mode for SFTP" );
171 }
172 catch ( SftpException e )
173 {
174 fireTransferDebug( "Not setting mode: " + e.getMessage() );
175 }
176 }
177
178 private void mkdirs( String resourceName, int mode )
179 throws SftpException, TransferFailedException
180 {
181 String[] dirs = PathUtils.dirnames( resourceName );
182 for ( int i = 0; i < dirs.length; i++ )
183 {
184 mkdir( dirs[i], mode );
185
186 channel.cd( dirs[i] );
187 }
188 }
189
190 private void mkdir( String dir, int mode )
191 throws TransferFailedException, SftpException
192 {
193 try
194 {
195 SftpATTRS attrs = channel.stat( dir );
196 if ( ( attrs.getPermissions() & S_IFDIR ) == 0 )
197 {
198 throw new TransferFailedException( "Remote path is not a directory:" + dir );
199 }
200 }
201 catch ( SftpException e )
202 {
203
204 channel.mkdir( dir );
205 if ( mode != -1 )
206 {
207 try
208 {
209 channel.chmod( mode, dir );
210 }
211 catch ( SftpException e1 )
212 {
213
214
215 }
216 }
217 }
218 }
219
220 private SftpATTRS changeToRepositoryDirectory( String dir, String filename )
221 throws ResourceDoesNotExistException, SftpException
222 {
223
224 SftpATTRS attrs;
225 try
226 {
227 channel.cd( repository.getBasedir() );
228
229 if ( dir.length() > 0 )
230 {
231 channel.cd( dir );
232 }
233
234 if ( filename.length() == 0 )
235 {
236 filename = ".";
237 }
238
239 attrs = channel.stat( filename );
240 }
241 catch ( SftpException e )
242 {
243 if ( e.toString().trim().endsWith( "No such file" ) )
244 {
245 throw new ResourceDoesNotExistException( e.toString(), e );
246 }
247 else if ( e.toString().trim().indexOf( "Can't change directory" ) != -1 )
248 {
249 throw new ResourceDoesNotExistException( e.toString(), e );
250 }
251 else
252 {
253 throw e;
254 }
255 }
256 return attrs;
257 }
258
259 public void putDirectory( File sourceDirectory, String destinationDirectory )
260 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
261 {
262 final RepositoryPermissions permissions = repository.getPermissions();
263
264 try
265 {
266 channel.cd( "/" );
267
268 String basedir = getRepository().getBasedir();
269 int directoryMode = getDirectoryMode( permissions );
270
271 mkdirs( basedir + "/", directoryMode );
272
273 fireTransferDebug( "Recursively uploading directory " + sourceDirectory.getAbsolutePath() + " as "
274 + destinationDirectory );
275
276 mkdirs( destinationDirectory, directoryMode );
277 ftpRecursivePut( sourceDirectory, null, ScpHelper.getResourceFilename( destinationDirectory ),
278 directoryMode );
279 }
280 catch ( SftpException e )
281 {
282 String msg =
283 "Error occurred while deploying '" + sourceDirectory.getAbsolutePath() + "' " + "to remote repository: "
284 + getRepository().getUrl() + ": " + e.getMessage();
285
286 throw new TransferFailedException( msg, e );
287 }
288 }
289
290 private void ftpRecursivePut( File sourceFile, String prefix, String fileName, int directoryMode )
291 throws TransferFailedException, SftpException
292 {
293 final RepositoryPermissions permissions = repository.getPermissions();
294
295 if ( sourceFile.isDirectory() )
296 {
297 if ( !fileName.equals( "." ) )
298 {
299 prefix = getFileName( prefix, fileName );
300 mkdir( fileName, directoryMode );
301 channel.cd( fileName );
302 }
303
304 File[] files = sourceFile.listFiles();
305 if ( files != null && files.length > 0 )
306 {
307
308 for ( int i = 0; i < files.length; i++ )
309 {
310 if ( files[i].isDirectory() )
311 {
312 ftpRecursivePut( files[i], prefix, files[i].getName(), directoryMode );
313 }
314 }
315 for ( int i = 0; i < files.length; i++ )
316 {
317 if ( !files[i].isDirectory() )
318 {
319 ftpRecursivePut( files[i], prefix, files[i].getName(), directoryMode );
320 }
321 }
322 }
323
324 channel.cd( ".." );
325 }
326 else
327 {
328 Resource resource = ScpHelper.getResource( getFileName( prefix, fileName ) );
329
330 firePutInitiated( resource, sourceFile );
331
332 putFile( sourceFile, resource, permissions );
333 }
334 }
335
336 private String getFileName( String prefix, String fileName )
337 {
338 if ( prefix != null )
339 {
340 prefix = prefix + "/" + fileName;
341 }
342 else
343 {
344 prefix = fileName;
345 }
346 return prefix;
347 }
348
349 public List getFileList( String destinationDirectory )
350 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
351 {
352 if ( destinationDirectory.length() == 0 )
353 {
354 destinationDirectory = ".";
355 }
356
357 String filename = ScpHelper.getResourceFilename( destinationDirectory );
358
359 String dir = ScpHelper.getResourceDirectory( destinationDirectory );
360
361
362 if ( dir.length() > 0 && dir.charAt( 0 ) == ScpHelper.PATH_SEPARATOR )
363 {
364 dir = dir.substring( 1 );
365 }
366
367 try
368 {
369 SftpATTRS attrs = changeToRepositoryDirectory( dir, filename );
370 if ( ( attrs.getPermissions() & S_IFDIR ) == 0 )
371 {
372 throw new TransferFailedException( "Remote path is not a directory:" + dir );
373 }
374
375 Vector fileList = channel.ls( filename );
376 List files = new ArrayList( fileList.size() );
377 for ( Iterator i = fileList.iterator(); i.hasNext(); )
378 {
379 ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) i.next();
380
381 String name = entry.getFilename();
382 if ( entry.getAttrs().isDir() )
383 {
384 if ( !name.equals( "." ) && !name.equals( ".." ) )
385 {
386 if ( !name.endsWith( "/" ) )
387 {
388 name += "/";
389 }
390 files.add( name );
391 }
392 }
393 else
394 {
395 files.add( name );
396 }
397 }
398 return files;
399 }
400 catch ( SftpException e )
401 {
402 String msg =
403 "Error occurred while listing '" + destinationDirectory + "' " + "on remote repository: "
404 + getRepository().getUrl() + ": " + e.getMessage();
405
406 throw new TransferFailedException( msg, e );
407 }
408 }
409
410 public boolean resourceExists( String resourceName )
411 throws TransferFailedException, AuthorizationException
412 {
413 String filename = ScpHelper.getResourceFilename( resourceName );
414
415 String dir = ScpHelper.getResourceDirectory( resourceName );
416
417
418 if ( dir.length() > 0 && dir.charAt( 0 ) == ScpHelper.PATH_SEPARATOR )
419 {
420 dir = dir.substring( 1 );
421 }
422
423 try
424 {
425 changeToRepositoryDirectory( dir, filename );
426
427 return true;
428 }
429 catch ( ResourceDoesNotExistException e )
430 {
431 return false;
432 }
433 catch ( SftpException e )
434 {
435 String msg =
436 "Error occurred while looking for '" + resourceName + "' " + "on remote repository: "
437 + getRepository().getUrl() + ": " + e.getMessage();
438
439 throw new TransferFailedException( msg, e );
440 }
441 }
442
443 protected void cleanupGetTransfer( Resource resource )
444 {
445 returnToParentDirectory( resource );
446 }
447
448 protected void cleanupPutTransfer( Resource resource )
449 {
450 returnToParentDirectory( resource );
451 }
452
453 protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
454 throws TransferFailedException
455 {
456 RepositoryPermissions permissions = getRepository().getPermissions();
457
458 String filename = ScpHelper.getResourceFilename( resource.getName() );
459 if ( permissions != null && permissions.getGroup() != null )
460 {
461 setGroup( filename, permissions );
462 }
463
464 if ( permissions != null && permissions.getFileMode() != null )
465 {
466 setFileMode( filename, permissions );
467 }
468 }
469
470 public void fillInputData( InputData inputData )
471 throws TransferFailedException, ResourceDoesNotExistException
472 {
473 Resource resource = inputData.getResource();
474
475 String filename = ScpHelper.getResourceFilename( resource.getName() );
476
477 String dir = ScpHelper.getResourceDirectory( resource.getName() );
478
479
480 if ( dir.length() > 0 && dir.charAt( 0 ) == ScpHelper.PATH_SEPARATOR )
481 {
482 dir = dir.substring( 1 );
483 }
484
485 try
486 {
487 SftpATTRS attrs = changeToRepositoryDirectory( dir, filename );
488
489 long lastModified = attrs.getMTime() * MILLIS_PER_SEC;
490 resource.setContentLength( attrs.getSize() );
491
492 resource.setLastModified( lastModified );
493
494 inputData.setInputStream( channel.get( filename ) );
495 }
496 catch ( SftpException e )
497 {
498 handleGetException( resource, e );
499 }
500 }
501
502 public void fillOutputData( OutputData outputData )
503 throws TransferFailedException
504 {
505 int directoryMode = getDirectoryMode( getRepository().getPermissions() );
506
507 Resource resource = outputData.getResource();
508
509 try
510 {
511 channel.cd( "/" );
512
513 String basedir = getRepository().getBasedir();
514 mkdirs( basedir + "/", directoryMode );
515
516 mkdirs( resource.getName(), directoryMode );
517
518 String filename = ScpHelper.getResourceFilename( resource.getName() );
519 outputData.setOutputStream( channel.put( filename ) );
520 }
521 catch ( TransferFailedException e )
522 {
523 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
524
525 throw e;
526 }
527 catch ( SftpException e )
528 {
529 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
530
531 String msg =
532 "Error occurred while deploying '" + resource.getName() + "' " + "to remote repository: "
533 + getRepository().getUrl() + ": " + e.getMessage();
534
535 throw new TransferFailedException( msg, e );
536 }
537 }
538
539
540
541
542
543
544 public int getDirectoryMode( RepositoryPermissions permissions )
545 {
546 int ret = -1;
547
548 if ( permissions != null )
549 {
550 ret = getOctalMode( permissions.getDirectoryMode() );
551 }
552
553 return ret;
554 }
555
556 public int getOctalMode( String mode )
557 {
558 int ret;
559 try
560 {
561 ret = Integer.valueOf( mode, 8 ).intValue();
562 }
563 catch ( NumberFormatException e )
564 {
565
566 fireTransferDebug( "the file mode must be a numerical mode for SFTP" );
567 ret = -1;
568 }
569 return ret;
570 }
571 }