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
59 public class ScpExternalWagon
60 extends AbstractWagon
61 implements CommandExecutor
62 {
63
64
65
66
67
68 private String scpExecutable = "scp";
69
70
71
72
73
74
75 private String sshExecutable = "ssh";
76
77
78
79
80
81
82 private String scpArgs;
83
84
85
86
87
88
89 private String sshArgs;
90
91 private ScpHelper sshTool = new ScpHelper( this );
92
93 private static final int SSH_FATAL_EXIT_CODE = 255;
94
95
96
97
98
99 protected void openConnectionInternal()
100 throws AuthenticationException
101 {
102 if ( authenticationInfo == null )
103 {
104 authenticationInfo = new AuthenticationInfo();
105 }
106 }
107
108 public void closeConnection()
109 {
110
111 }
112
113 public boolean getIfNewer( String resourceName, File destination, long timestamp )
114 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
115 {
116 fireSessionDebug( "getIfNewer in SCP wagon is not supported - performing an unconditional get" );
117 get( resourceName, destination );
118 return true;
119 }
120
121
122
123
124
125 private String buildRemoteHost()
126 {
127 String username = this.getRepository().getUsername();
128 if ( username == null )
129 {
130 username = authenticationInfo.getUserName();
131 }
132
133 if ( username == null )
134 {
135 return getRepository().getHost();
136 }
137 else
138 {
139 return username + "@" + getRepository().getHost();
140 }
141 }
142
143 public void executeCommand( String command )
144 throws CommandExecutionException
145 {
146 fireTransferDebug( "Executing command: " + command );
147
148 executeCommand( command, false );
149 }
150
151 public Streams executeCommand( String command, boolean ignoreFailures )
152 throws CommandExecutionException
153 {
154 boolean putty = isPuTTY();
155
156 File privateKey;
157 try
158 {
159 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
160 }
161 catch ( FileNotFoundException e )
162 {
163 throw new CommandExecutionException( e.getMessage(), e );
164 }
165 Commandline cl = createBaseCommandLine( putty, sshExecutable, privateKey );
166
167 int port =
168 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
169 if ( port != ScpHelper.DEFAULT_SSH_PORT )
170 {
171 if ( putty )
172 {
173 cl.createArg().setLine( "-P " + port );
174 }
175 else
176 {
177 cl.createArg().setLine( "-p " + port );
178 }
179 }
180
181 if ( sshArgs != null )
182 {
183 cl.createArg().setLine( sshArgs );
184 }
185
186 String remoteHost = this.buildRemoteHost();
187
188 cl.createArg().setValue( remoteHost );
189
190 cl.createArg().setValue( command );
191
192 fireSessionDebug( "Executing command: " + cl.toString() );
193
194 try
195 {
196 CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
197 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
198 int exitCode = CommandLineUtils.executeCommandLine( cl, out, err );
199 Streams streams = new Streams();
200 streams.setOut( out.getOutput() );
201 streams.setErr( err.getOutput() );
202 fireSessionDebug( streams.getOut() );
203 fireSessionDebug( streams.getErr() );
204 if ( exitCode != 0 )
205 {
206 if ( !ignoreFailures || exitCode == SSH_FATAL_EXIT_CODE )
207 {
208 throw new CommandExecutionException( "Exit code " + exitCode + " - " + err.getOutput() );
209 }
210 }
211 return streams;
212 }
213 catch ( CommandLineException e )
214 {
215 throw new CommandExecutionException( "Error executing command line", e );
216 }
217 }
218
219 protected boolean isPuTTY()
220 {
221 return sshExecutable.toLowerCase( Locale.ENGLISH ).indexOf( "plink" ) >= 0;
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 && err.getOutput().trim().toLowerCase( Locale.ENGLISH ).indexOf( "no such file or directory" )
315 != -1 )
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 return scpExecutable.toLowerCase( Locale.ENGLISH ).indexOf( "pscp" ) >= 0;
341 }
342
343 private String normalizeResource( Resource resource )
344 {
345 return StringUtils.replace( resource.getName(), "\\", "/" );
346 }
347
348 public void put( File source, String destination )
349 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
350 {
351 Resource resource = new Resource( destination );
352
353 firePutInitiated( resource, source );
354
355 if ( !source.exists() )
356 {
357 throw new ResourceDoesNotExistException( "Specified source file does not exist: " + source );
358 }
359
360 String basedir = getRepository().getBasedir();
361
362 String resourceName = StringUtils.replace( destination, "\\", "/" );
363
364 String dir = PathUtils.dirname( resourceName );
365
366 dir = StringUtils.replace( dir, "\\", "/" );
367
368 String umaskCmd = null;
369 if ( getRepository().getPermissions() != null )
370 {
371 String dirPerms = getRepository().getPermissions().getDirectoryMode();
372
373 if ( dirPerms != null )
374 {
375 umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
376 }
377 }
378
379 String mkdirCmd = "mkdir -p " + basedir + "/" + dir + "\n";
380
381 if ( umaskCmd != null )
382 {
383 mkdirCmd = umaskCmd + "; " + mkdirCmd;
384 }
385
386 try
387 {
388 executeCommand( mkdirCmd );
389 }
390 catch ( CommandExecutionException e )
391 {
392 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
393
394 throw new TransferFailedException( "Error executing command for transfer", e );
395 }
396
397 resource.setContentLength( source.length() );
398
399 resource.setLastModified( source.lastModified() );
400
401 firePutStarted( resource, source );
402
403 executeScpCommand( resource, source, true );
404
405 postProcessListeners( resource, source, TransferEvent.REQUEST_PUT );
406
407 try
408 {
409 RepositoryPermissions permissions = getRepository().getPermissions();
410
411 if ( permissions != null && permissions.getGroup() != null )
412 {
413 executeCommand( "chgrp -f " + permissions.getGroup() + " " + basedir + "/" + resourceName + "\n",
414 true );
415 }
416
417 if ( permissions != null && permissions.getFileMode() != null )
418 {
419 executeCommand( "chmod -f " + permissions.getFileMode() + " " + basedir + "/" + resourceName + "\n",
420 true );
421 }
422 }
423 catch ( CommandExecutionException e )
424 {
425 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
426
427 throw new TransferFailedException( "Error executing command for transfer", e );
428 }
429 firePutCompleted( resource, source );
430 }
431
432 public void get( String resourceName, File destination )
433 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
434 {
435 String path = StringUtils.replace( resourceName, "\\", "/" );
436
437 Resource resource = new Resource( path );
438
439 fireGetInitiated( resource, destination );
440
441 createParentDirectories( destination );
442
443 fireGetStarted( resource, destination );
444
445 executeScpCommand( resource, destination, false );
446
447 postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
448
449 fireGetCompleted( resource, destination );
450 }
451
452
453
454
455
456
457
458
459 public List<String> getFileList( String destinationDirectory )
460 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
461 {
462 return sshTool.getFileList( destinationDirectory, repository );
463 }
464
465 public void putDirectory( File sourceDirectory, String destinationDirectory )
466 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
467 {
468 sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
469 }
470
471 public boolean resourceExists( String resourceName )
472 throws TransferFailedException, AuthorizationException
473 {
474 return sshTool.resourceExists( resourceName, repository );
475 }
476
477 public boolean supportsDirectoryCopy()
478 {
479 return true;
480 }
481
482 public String getScpExecutable()
483 {
484 return scpExecutable;
485 }
486
487 public void setScpExecutable( String scpExecutable )
488 {
489 this.scpExecutable = scpExecutable;
490 }
491
492 public String getSshExecutable()
493 {
494 return sshExecutable;
495 }
496
497 public void setSshExecutable( String sshExecutable )
498 {
499 this.sshExecutable = sshExecutable;
500 }
501
502 public String getScpArgs()
503 {
504 return scpArgs;
505 }
506
507 public void setScpArgs( String scpArgs )
508 {
509 this.scpArgs = scpArgs;
510 }
511
512 public String getSshArgs()
513 {
514 return sshArgs;
515 }
516
517 public void setSshArgs( String sshArgs )
518 {
519 this.sshArgs = sshArgs;
520 }
521 }