1 package org.apache.maven.wagon.providers.ssh.external;
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.wagon.AbstractWagon;
23 import org.apache.maven.wagon.CommandExecutionException;
24 import org.apache.maven.wagon.CommandExecutor;
25 import org.apache.maven.wagon.PathUtils;
26 import org.apache.maven.wagon.PermissionModeUtils;
27 import org.apache.maven.wagon.ResourceDoesNotExistException;
28 import org.apache.maven.wagon.Streams;
29 import org.apache.maven.wagon.TransferFailedException;
30 import org.apache.maven.wagon.WagonConstants;
31 import org.apache.maven.wagon.authentication.AuthenticationException;
32 import org.apache.maven.wagon.authentication.AuthenticationInfo;
33 import org.apache.maven.wagon.authorization.AuthorizationException;
34 import org.apache.maven.wagon.events.TransferEvent;
35 import org.apache.maven.wagon.providers.ssh.ScpHelper;
36 import org.apache.maven.wagon.repository.RepositoryPermissions;
37 import org.apache.maven.wagon.resource.Resource;
38 import org.codehaus.plexus.util.StringUtils;
39 import org.codehaus.plexus.util.cli.CommandLineException;
40 import org.codehaus.plexus.util.cli.CommandLineUtils;
41 import org.codehaus.plexus.util.cli.Commandline;
42
43 import java.io.File;
44 import java.io.FileNotFoundException;
45 import java.util.List;
46 import java.util.Locale;
47
48
49
50
51
52
53
54
55
56
57
58 public class ScpExternalWagon
59 extends AbstractWagon
60 implements CommandExecutor
61 {
62
63
64
65
66
67 private String scpExecutable = "scp";
68
69
70
71
72
73
74 private String sshExecutable = "ssh";
75
76
77
78
79
80
81 private String scpArgs;
82
83
84
85
86
87
88 private String sshArgs;
89
90 private ScpHelper sshTool = new ScpHelper( this );
91
92 private static final int SSH_FATAL_EXIT_CODE = 255;
93
94
95
96
97
98 protected void openConnectionInternal()
99 throws AuthenticationException
100 {
101 if ( authenticationInfo == null )
102 {
103 authenticationInfo = new AuthenticationInfo();
104 }
105 }
106
107 public void closeConnection()
108 {
109
110 }
111
112 public boolean getIfNewer( String resourceName, File destination, long timestamp )
113 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
114 {
115 fireSessionDebug( "getIfNewer in SCP wagon is not supported - performing an unconditional get" );
116 get( resourceName, destination );
117 return true;
118 }
119
120
121
122
123
124 private String buildRemoteHost()
125 {
126 String username = this.getRepository().getUsername();
127 if ( username == null )
128 {
129 username = authenticationInfo.getUserName();
130 }
131
132 if ( username == null )
133 {
134 return getRepository().getHost();
135 }
136 else
137 {
138 return username + "@" + getRepository().getHost();
139 }
140 }
141
142 public void executeCommand( String command )
143 throws CommandExecutionException
144 {
145 fireTransferDebug( "Executing command: " + command );
146
147 executeCommand( command, false );
148 }
149
150 public Streams executeCommand( String command, boolean ignoreFailures )
151 throws CommandExecutionException
152 {
153 boolean putty = isPuTTY();
154
155 File privateKey;
156 try
157 {
158 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
159 }
160 catch ( FileNotFoundException e )
161 {
162 throw new CommandExecutionException( e.getMessage(), e );
163 }
164 Commandline cl = createBaseCommandLine( putty, sshExecutable, privateKey );
165
166 int port =
167 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
168 if ( port != ScpHelper.DEFAULT_SSH_PORT )
169 {
170 if ( putty )
171 {
172 cl.createArg().setLine( "-P " + port );
173 }
174 else
175 {
176 cl.createArg().setLine( "-p " + port );
177 }
178 }
179
180 if ( sshArgs != null )
181 {
182 cl.createArg().setLine( sshArgs );
183 }
184
185 String remoteHost = this.buildRemoteHost();
186
187 cl.createArg().setValue( remoteHost );
188
189 cl.createArg().setValue( command );
190
191 fireSessionDebug( "Executing command: " + cl.toString() );
192
193 try
194 {
195 CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
196 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
197 int exitCode = CommandLineUtils.executeCommandLine( cl, out, err );
198 Streams streams = new Streams();
199 streams.setOut( out.getOutput() );
200 streams.setErr( err.getOutput() );
201 fireSessionDebug( streams.getOut() );
202 fireSessionDebug( streams.getErr() );
203 if ( exitCode != 0 )
204 {
205 if ( !ignoreFailures || exitCode == SSH_FATAL_EXIT_CODE )
206 {
207 throw new CommandExecutionException( "Exit code " + exitCode + " - " + err.getOutput() );
208 }
209 }
210 return streams;
211 }
212 catch ( CommandLineException e )
213 {
214 throw new CommandExecutionException( "Error executing command line", e );
215 }
216 }
217
218 protected boolean isPuTTY()
219 {
220 return sshExecutable.toLowerCase( Locale.ENGLISH ).contains( "plink" );
221 }
222
223 private Commandline createBaseCommandLine( boolean putty, String executable, File privateKey )
224 {
225 Commandline cl = new Commandline();
226
227 cl.setExecutable( executable );
228
229 if ( privateKey != null )
230 {
231 cl.createArg().setValue( "-i" );
232 cl.createArg().setFile( privateKey );
233 }
234
235 String password = authenticationInfo.getPassword();
236 if ( putty && password != null )
237 {
238 cl.createArg().setValue( "-pw" );
239 cl.createArg().setValue( password );
240 }
241
242
243 if ( putty )
244 {
245 cl.createArg().setValue( "-batch" );
246 }
247 else
248 {
249 cl.createArg().setValue( "-o" );
250 cl.createArg().setValue( "BatchMode yes" );
251 }
252 return cl;
253 }
254
255
256 private void executeScpCommand( Resource resource, File localFile, boolean put )
257 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
258 {
259 boolean putty = isPuTTYSCP();
260
261 File privateKey;
262 try
263 {
264 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
265 }
266 catch ( FileNotFoundException e )
267 {
268 fireSessionConnectionRefused();
269
270 throw new AuthorizationException( e.getMessage() );
271 }
272 Commandline cl = createBaseCommandLine( putty, scpExecutable, privateKey );
273
274 cl.setWorkingDirectory( localFile.getParentFile().getAbsolutePath() );
275
276 int port =
277 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
278 if ( port != ScpHelper.DEFAULT_SSH_PORT )
279 {
280 cl.createArg().setLine( "-P " + port );
281 }
282
283 if ( scpArgs != null )
284 {
285 cl.createArg().setLine( scpArgs );
286 }
287
288 String resourceName = normalizeResource( resource );
289 String remoteFile = getRepository().getBasedir() + "/" + resourceName;
290
291 remoteFile = StringUtils.replace( remoteFile, " ", "\\ " );
292
293 String qualifiedRemoteFile = this.buildRemoteHost() + ":" + remoteFile;
294 if ( put )
295 {
296 cl.createArg().setValue( localFile.getName() );
297 cl.createArg().setValue( qualifiedRemoteFile );
298 }
299 else
300 {
301 cl.createArg().setValue( qualifiedRemoteFile );
302 cl.createArg().setValue( localFile.getName() );
303 }
304
305 fireSessionDebug( "Executing command: " + cl.toString() );
306
307 try
308 {
309 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
310 int exitCode = CommandLineUtils.executeCommandLine( cl, null, err );
311 if ( exitCode != 0 )
312 {
313 if ( !put && err.getOutput().trim().toLowerCase( Locale.ENGLISH ).contains( "no such file or directory" ))
314 {
315 throw new ResourceDoesNotExistException( err.getOutput() );
316 }
317 else
318 {
319 TransferFailedException e =
320 new TransferFailedException( "Exit code: " + exitCode + " - " + err.getOutput() );
321
322 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
323
324 throw e;
325 }
326 }
327 }
328 catch ( CommandLineException e )
329 {
330 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
331
332 throw new TransferFailedException( "Error executing command line", e );
333 }
334 }
335
336 boolean isPuTTYSCP()
337 {
338 return scpExecutable.toLowerCase( Locale.ENGLISH ).contains( "pscp" );
339 }
340
341 private String normalizeResource( Resource resource )
342 {
343 return StringUtils.replace( resource.getName(), "\\", "/" );
344 }
345
346 public void put( File source, String destination )
347 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
348 {
349 Resource resource = new Resource( destination );
350
351 firePutInitiated( resource, source );
352
353 if ( !source.exists() )
354 {
355 throw new ResourceDoesNotExistException( "Specified source file does not exist: " + source );
356 }
357
358 String basedir = getRepository().getBasedir();
359
360 String resourceName = StringUtils.replace( destination, "\\", "/" );
361
362 String dir = PathUtils.dirname( resourceName );
363
364 dir = StringUtils.replace( dir, "\\", "/" );
365
366 String umaskCmd = null;
367 if ( getRepository().getPermissions() != null )
368 {
369 String dirPerms = getRepository().getPermissions().getDirectoryMode();
370
371 if ( dirPerms != null )
372 {
373 umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
374 }
375 }
376
377 String mkdirCmd = "mkdir -p " + basedir + "/" + dir + "\n";
378
379 if ( umaskCmd != null )
380 {
381 mkdirCmd = umaskCmd + "; " + mkdirCmd;
382 }
383
384 try
385 {
386 executeCommand( mkdirCmd );
387 }
388 catch ( CommandExecutionException e )
389 {
390 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
391
392 throw new TransferFailedException( "Error executing command for transfer", e );
393 }
394
395 resource.setContentLength( source.length() );
396
397 resource.setLastModified( source.lastModified() );
398
399 firePutStarted( resource, source );
400
401 executeScpCommand( resource, source, true );
402
403 postProcessListeners( resource, source, TransferEvent.REQUEST_PUT );
404
405 try
406 {
407 RepositoryPermissions permissions = getRepository().getPermissions();
408
409 if ( permissions != null && permissions.getGroup() != null )
410 {
411 executeCommand( "chgrp -f " + permissions.getGroup() + " " + basedir + "/" + resourceName + "\n",
412 true );
413 }
414
415 if ( permissions != null && permissions.getFileMode() != null )
416 {
417 executeCommand( "chmod -f " + permissions.getFileMode() + " " + basedir + "/" + resourceName + "\n",
418 true );
419 }
420 }
421 catch ( CommandExecutionException e )
422 {
423 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
424
425 throw new TransferFailedException( "Error executing command for transfer", e );
426 }
427 firePutCompleted( resource, source );
428 }
429
430 public void get( String resourceName, File destination )
431 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
432 {
433 String path = StringUtils.replace( resourceName, "\\", "/" );
434
435 Resource resource = new Resource( path );
436
437 fireGetInitiated( resource, destination );
438
439 createParentDirectories( destination );
440
441 fireGetStarted( resource, destination );
442
443 executeScpCommand( resource, destination, false );
444
445 postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
446
447 fireGetCompleted( resource, destination );
448 }
449
450
451
452
453
454
455
456
457 public List<String> getFileList( String destinationDirectory )
458 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
459 {
460 return sshTool.getFileList( destinationDirectory, repository );
461 }
462
463 public void putDirectory( File sourceDirectory, String destinationDirectory )
464 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
465 {
466 sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
467 }
468
469 public boolean resourceExists( String resourceName )
470 throws TransferFailedException, AuthorizationException
471 {
472 return sshTool.resourceExists( resourceName, repository );
473 }
474
475 public boolean supportsDirectoryCopy()
476 {
477 return true;
478 }
479
480 public String getScpExecutable()
481 {
482 return scpExecutable;
483 }
484
485 public void setScpExecutable( String scpExecutable )
486 {
487 this.scpExecutable = scpExecutable;
488 }
489
490 public String getSshExecutable()
491 {
492 return sshExecutable;
493 }
494
495 public void setSshExecutable( String sshExecutable )
496 {
497 this.sshExecutable = sshExecutable;
498 }
499
500 public String getScpArgs()
501 {
502 return scpArgs;
503 }
504
505 public void setScpArgs( String scpArgs )
506 {
507 this.scpArgs = scpArgs;
508 }
509
510 public String getSshArgs()
511 {
512 return sshArgs;
513 }
514
515 public void setSshArgs( String sshArgs )
516 {
517 this.sshArgs = sshArgs;
518 }
519 }