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
314 && err.getOutput().trim().toLowerCase( Locale.ENGLISH ).contains( "no such file or directory" ) )
315 {
316 throw new ResourceDoesNotExistException( err.getOutput() );
317 }
318 else
319 {
320 TransferFailedException e =
321 new TransferFailedException( "Exit code: " + exitCode + " - " + err.getOutput() );
322
323 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
324
325 throw e;
326 }
327 }
328 }
329 catch ( CommandLineException e )
330 {
331 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
332
333 throw new TransferFailedException( "Error executing command line", e );
334 }
335 }
336
337 boolean isPuTTYSCP()
338 {
339 return scpExecutable.toLowerCase( Locale.ENGLISH ).contains( "pscp" );
340 }
341
342 private String normalizeResource( Resource resource )
343 {
344 return StringUtils.replace( resource.getName(), "\\", "/" );
345 }
346
347 public void put( File source, String destination )
348 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
349 {
350 Resource resource = new Resource( destination );
351
352 firePutInitiated( resource, source );
353
354 if ( !source.exists() )
355 {
356 throw new ResourceDoesNotExistException( "Specified source file does not exist: " + source );
357 }
358
359 String basedir = getRepository().getBasedir();
360
361 String resourceName = StringUtils.replace( destination, "\\", "/" );
362
363 String dir = PathUtils.dirname( resourceName );
364
365 dir = StringUtils.replace( dir, "\\", "/" );
366
367 String umaskCmd = null;
368 if ( getRepository().getPermissions() != null )
369 {
370 String dirPerms = getRepository().getPermissions().getDirectoryMode();
371
372 if ( dirPerms != null )
373 {
374 umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
375 }
376 }
377
378 String mkdirCmd = "mkdir -p " + basedir + "/" + dir + "\n";
379
380 if ( umaskCmd != null )
381 {
382 mkdirCmd = umaskCmd + "; " + mkdirCmd;
383 }
384
385 try
386 {
387 executeCommand( mkdirCmd );
388 }
389 catch ( CommandExecutionException e )
390 {
391 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
392
393 throw new TransferFailedException( "Error executing command for transfer", e );
394 }
395
396 resource.setContentLength( source.length() );
397
398 resource.setLastModified( source.lastModified() );
399
400 firePutStarted( resource, source );
401
402 executeScpCommand( resource, source, true );
403
404 postProcessListeners( resource, source, TransferEvent.REQUEST_PUT );
405
406 try
407 {
408 RepositoryPermissions permissions = getRepository().getPermissions();
409
410 if ( permissions != null && permissions.getGroup() != null )
411 {
412 executeCommand( "chgrp -f " + permissions.getGroup() + " " + basedir + "/" + resourceName + "\n",
413 true );
414 }
415
416 if ( permissions != null && permissions.getFileMode() != null )
417 {
418 executeCommand( "chmod -f " + permissions.getFileMode() + " " + basedir + "/" + resourceName + "\n",
419 true );
420 }
421 }
422 catch ( CommandExecutionException e )
423 {
424 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
425
426 throw new TransferFailedException( "Error executing command for transfer", e );
427 }
428 firePutCompleted( resource, source );
429 }
430
431 public void get( String resourceName, File destination )
432 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
433 {
434 String path = StringUtils.replace( resourceName, "\\", "/" );
435
436 Resource resource = new Resource( path );
437
438 fireGetInitiated( resource, destination );
439
440 createParentDirectories( destination );
441
442 fireGetStarted( resource, destination );
443
444 executeScpCommand( resource, destination, false );
445
446 postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
447
448 fireGetCompleted( resource, destination );
449 }
450
451
452
453
454
455
456
457
458 public List<String> getFileList( String destinationDirectory )
459 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
460 {
461 return sshTool.getFileList( destinationDirectory, repository );
462 }
463
464 public void putDirectory( File sourceDirectory, String destinationDirectory )
465 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
466 {
467 sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
468 }
469
470 public boolean resourceExists( String resourceName )
471 throws TransferFailedException, AuthorizationException
472 {
473 return sshTool.resourceExists( resourceName, repository );
474 }
475
476 public boolean supportsDirectoryCopy()
477 {
478 return true;
479 }
480
481 public String getScpExecutable()
482 {
483 return scpExecutable;
484 }
485
486 public void setScpExecutable( String scpExecutable )
487 {
488 this.scpExecutable = scpExecutable;
489 }
490
491 public String getSshExecutable()
492 {
493 return sshExecutable;
494 }
495
496 public void setSshExecutable( String sshExecutable )
497 {
498 this.sshExecutable = sshExecutable;
499 }
500
501 public String getScpArgs()
502 {
503 return scpArgs;
504 }
505
506 public void setScpArgs( String scpArgs )
507 {
508 this.scpArgs = scpArgs;
509 }
510
511 public String getSshArgs()
512 {
513 return sshArgs;
514 }
515
516 public void setSshArgs( String sshArgs )
517 {
518 this.sshArgs = sshArgs;
519 }
520 }