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 String exe = sshExecutable.toLowerCase( Locale.ENGLISH );
221 return exe.contains( "plink" ) || exe.contains( "klink" );
222 }
223
224 private Commandline createBaseCommandLine( boolean putty, String executable, File privateKey )
225 {
226 Commandline cl = new Commandline();
227
228 cl.setExecutable( executable );
229
230 if ( privateKey != null )
231 {
232 cl.createArg().setValue( "-i" );
233 cl.createArg().setFile( privateKey );
234 }
235
236 String password = authenticationInfo.getPassword();
237 if ( putty && password != null )
238 {
239 cl.createArg().setValue( "-pw" );
240 cl.createArg().setValue( password );
241 }
242
243
244 if ( putty )
245 {
246 cl.createArg().setValue( "-batch" );
247 }
248 else
249 {
250 cl.createArg().setValue( "-o" );
251 cl.createArg().setValue( "BatchMode yes" );
252 }
253 return cl;
254 }
255
256
257 private void executeScpCommand( Resource resource, File localFile, boolean put )
258 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
259 {
260 boolean putty = isPuTTYSCP();
261
262 File privateKey;
263 try
264 {
265 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
266 }
267 catch ( FileNotFoundException e )
268 {
269 fireSessionConnectionRefused();
270
271 throw new AuthorizationException( e.getMessage() );
272 }
273 Commandline cl = createBaseCommandLine( putty, scpExecutable, privateKey );
274
275 cl.setWorkingDirectory( localFile.getParentFile().getAbsolutePath() );
276
277 int port =
278 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
279 if ( port != ScpHelper.DEFAULT_SSH_PORT )
280 {
281 cl.createArg().setLine( "-P " + port );
282 }
283
284 if ( scpArgs != null )
285 {
286 cl.createArg().setLine( scpArgs );
287 }
288
289 String resourceName = normalizeResource( resource );
290 String remoteFile = getRepository().getBasedir() + "/" + resourceName;
291
292 remoteFile = StringUtils.replace( remoteFile, " ", "\\ " );
293
294 String qualifiedRemoteFile = this.buildRemoteHost() + ":" + remoteFile;
295 if ( put )
296 {
297 cl.createArg().setValue( localFile.getName() );
298 cl.createArg().setValue( qualifiedRemoteFile );
299 }
300 else
301 {
302 cl.createArg().setValue( qualifiedRemoteFile );
303 cl.createArg().setValue( localFile.getName() );
304 }
305
306 fireSessionDebug( "Executing command: " + cl.toString() );
307
308 try
309 {
310 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
311 int exitCode = CommandLineUtils.executeCommandLine( cl, null, err );
312 if ( exitCode != 0 )
313 {
314 if ( !put
315 && err.getOutput().trim().toLowerCase( Locale.ENGLISH ).contains( "no such file or directory" ) )
316 {
317 throw new ResourceDoesNotExistException( err.getOutput() );
318 }
319 else
320 {
321 TransferFailedException e =
322 new TransferFailedException( "Exit code: " + exitCode + " - " + err.getOutput() );
323
324 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
325
326 throw e;
327 }
328 }
329 }
330 catch ( CommandLineException e )
331 {
332 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
333
334 throw new TransferFailedException( "Error executing command line", e );
335 }
336 }
337
338 boolean isPuTTYSCP()
339 {
340 String exe = scpExecutable.toLowerCase( Locale.ENGLISH );
341 return exe.contains( "pscp" ) || exe.contains( "kscp" );
342 }
343
344 private String normalizeResource( Resource resource )
345 {
346 return StringUtils.replace( resource.getName(), "\\", "/" );
347 }
348
349 public void put( File source, String destination )
350 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
351 {
352 Resource resource = new Resource( destination );
353
354 firePutInitiated( resource, source );
355
356 if ( !source.exists() )
357 {
358 throw new ResourceDoesNotExistException( "Specified source file does not exist: " + source );
359 }
360
361 String basedir = getRepository().getBasedir();
362
363 String resourceName = StringUtils.replace( destination, "\\", "/" );
364
365 String dir = PathUtils.dirname( resourceName );
366
367 dir = StringUtils.replace( dir, "\\", "/" );
368
369 String umaskCmd = null;
370 if ( getRepository().getPermissions() != null )
371 {
372 String dirPerms = getRepository().getPermissions().getDirectoryMode();
373
374 if ( dirPerms != null )
375 {
376 umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
377 }
378 }
379
380 String mkdirCmd = "mkdir -p " + basedir + "/" + dir + "\n";
381
382 if ( umaskCmd != null )
383 {
384 mkdirCmd = umaskCmd + "; " + mkdirCmd;
385 }
386
387 try
388 {
389 executeCommand( mkdirCmd );
390 }
391 catch ( CommandExecutionException e )
392 {
393 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
394
395 throw new TransferFailedException( "Error executing command for transfer", e );
396 }
397
398 resource.setContentLength( source.length() );
399
400 resource.setLastModified( source.lastModified() );
401
402 firePutStarted( resource, source );
403
404 executeScpCommand( resource, source, true );
405
406 postProcessListeners( resource, source, TransferEvent.REQUEST_PUT );
407
408 try
409 {
410 RepositoryPermissions permissions = getRepository().getPermissions();
411
412 if ( permissions != null && permissions.getGroup() != null )
413 {
414 executeCommand( "chgrp -f " + permissions.getGroup() + " " + basedir + "/" + resourceName + "\n",
415 true );
416 }
417
418 if ( permissions != null && permissions.getFileMode() != null )
419 {
420 executeCommand( "chmod -f " + permissions.getFileMode() + " " + basedir + "/" + resourceName + "\n",
421 true );
422 }
423 }
424 catch ( CommandExecutionException e )
425 {
426 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
427
428 throw new TransferFailedException( "Error executing command for transfer", e );
429 }
430 firePutCompleted( resource, source );
431 }
432
433 public void get( String resourceName, File destination )
434 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
435 {
436 String path = StringUtils.replace( resourceName, "\\", "/" );
437
438 Resource resource = new Resource( path );
439
440 fireGetInitiated( resource, destination );
441
442 createParentDirectories( destination );
443
444 fireGetStarted( resource, destination );
445
446 executeScpCommand( resource, destination, false );
447
448 postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
449
450 fireGetCompleted( resource, destination );
451 }
452
453
454
455
456
457
458
459
460 public List<String> getFileList( String destinationDirectory )
461 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
462 {
463 return sshTool.getFileList( destinationDirectory, repository );
464 }
465
466 public void putDirectory( File sourceDirectory, String destinationDirectory )
467 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
468 {
469 sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
470 }
471
472 public boolean resourceExists( String resourceName )
473 throws TransferFailedException, AuthorizationException
474 {
475 return sshTool.resourceExists( resourceName, repository );
476 }
477
478 public boolean supportsDirectoryCopy()
479 {
480 return true;
481 }
482
483 public String getScpExecutable()
484 {
485 return scpExecutable;
486 }
487
488 public void setScpExecutable( String scpExecutable )
489 {
490 this.scpExecutable = scpExecutable;
491 }
492
493 public String getSshExecutable()
494 {
495 return sshExecutable;
496 }
497
498 public void setSshExecutable( String sshExecutable )
499 {
500 this.sshExecutable = sshExecutable;
501 }
502
503 public String getScpArgs()
504 {
505 return scpArgs;
506 }
507
508 public void setScpArgs( String scpArgs )
509 {
510 this.scpArgs = scpArgs;
511 }
512
513 public String getSshArgs()
514 {
515 return sshArgs;
516 }
517
518 public void setSshArgs( String sshArgs )
519 {
520 this.sshArgs = sshArgs;
521 }
522 }