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 java.io.File;
23 import java.io.FileNotFoundException;
24 import java.util.List;
25 import java.util.Locale;
26
27 import org.apache.maven.wagon.AbstractWagon;
28 import org.apache.maven.wagon.CommandExecutionException;
29 import org.apache.maven.wagon.CommandExecutor;
30 import org.apache.maven.wagon.PathUtils;
31 import org.apache.maven.wagon.PermissionModeUtils;
32 import org.apache.maven.wagon.ResourceDoesNotExistException;
33 import org.apache.maven.wagon.Streams;
34 import org.apache.maven.wagon.TransferFailedException;
35 import org.apache.maven.wagon.WagonConstants;
36 import org.apache.maven.wagon.authentication.AuthenticationException;
37 import org.apache.maven.wagon.authentication.AuthenticationInfo;
38 import org.apache.maven.wagon.authorization.AuthorizationException;
39 import org.apache.maven.wagon.events.TransferEvent;
40 import org.apache.maven.wagon.providers.ssh.ScpHelper;
41 import org.apache.maven.wagon.repository.RepositoryPermissions;
42 import org.apache.maven.wagon.resource.Resource;
43 import org.codehaus.plexus.util.StringUtils;
44 import org.codehaus.plexus.util.cli.CommandLineException;
45 import org.codehaus.plexus.util.cli.CommandLineUtils;
46 import org.codehaus.plexus.util.cli.Commandline;
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class ScpExternalWagon
61 extends AbstractWagon
62 implements CommandExecutor
63 {
64
65
66
67
68
69 private String scpExecutable = "scp";
70
71
72
73
74
75
76 private String sshExecutable = "ssh";
77
78
79
80
81
82
83 private String scpArgs;
84
85
86
87
88
89
90 private String sshArgs;
91
92 private ScpHelper sshTool = new ScpHelper( this );
93
94 private static final int SSH_FATAL_EXIT_CODE = 255;
95
96
97
98
99
100 protected void openConnectionInternal()
101 throws AuthenticationException
102 {
103 if ( authenticationInfo == null )
104 {
105 authenticationInfo = new AuthenticationInfo();
106 }
107 }
108
109 public void closeConnection()
110 {
111
112 }
113
114 public boolean getIfNewer( String resourceName, File destination, long timestamp )
115 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
116 {
117 fireSessionDebug( "getIfNewer in SCP wagon is not supported - performing an unconditional get" );
118 get( resourceName, destination );
119 return true;
120 }
121
122
123
124
125
126 private String buildRemoteHost()
127 {
128 String username = this.getRepository().getUsername();
129 if ( username == null )
130 {
131 username = authenticationInfo.getUserName();
132 }
133
134 if ( username == null )
135 {
136 return getRepository().getHost();
137 }
138 else
139 {
140 return username + "@" + getRepository().getHost();
141 }
142 }
143
144 public void executeCommand( String command )
145 throws CommandExecutionException
146 {
147 fireTransferDebug( "Executing command: " + command );
148
149 executeCommand( command, false );
150 }
151
152 public Streams executeCommand( String command, boolean ignoreFailures )
153 throws CommandExecutionException
154 {
155 boolean putty = isPuTTY();
156
157 File privateKey;
158 try
159 {
160 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
161 }
162 catch ( FileNotFoundException e )
163 {
164 throw new CommandExecutionException( e.getMessage() );
165 }
166 Commandline cl = createBaseCommandLine( putty, sshExecutable, privateKey );
167
168 int port =
169 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
170 if ( port != ScpHelper.DEFAULT_SSH_PORT )
171 {
172 if ( putty )
173 {
174 cl.createArgument().setLine( "-P " + port );
175 }
176 else
177 {
178 cl.createArgument().setLine( "-p " + port );
179 }
180 }
181
182 if ( sshArgs != null )
183 {
184 cl.createArgument().setLine( sshArgs );
185 }
186
187 String remoteHost = this.buildRemoteHost();
188
189 cl.createArgument().setValue( remoteHost );
190
191 cl.createArgument().setValue( command );
192
193 fireSessionDebug( "Executing command: " + cl.toString() );
194
195 try
196 {
197 CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
198 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
199 int exitCode = CommandLineUtils.executeCommandLine( cl, out, err );
200 Streams streams = new Streams();
201 streams.setOut( out.getOutput() );
202 streams.setErr( err.getOutput() );
203 fireSessionDebug( streams.getOut() );
204 fireSessionDebug( streams.getErr() );
205 if ( exitCode != 0 )
206 {
207 if ( !ignoreFailures || exitCode == SSH_FATAL_EXIT_CODE )
208 {
209 throw new CommandExecutionException( "Exit code " + exitCode + " - " + err.getOutput() );
210 }
211 }
212 return streams;
213 }
214 catch ( CommandLineException e )
215 {
216 throw new CommandExecutionException( "Error executing command line", e );
217 }
218 }
219
220 protected boolean isPuTTY()
221 {
222 return sshExecutable.toLowerCase( Locale.ENGLISH ).indexOf( "plink" ) >= 0;
223 }
224
225 private Commandline createBaseCommandLine( boolean putty, String executable, File privateKey )
226 {
227 Commandline cl = new Commandline();
228
229 cl.setExecutable( executable );
230
231 if ( privateKey != null )
232 {
233 cl.createArgument().setValue( "-i" );
234 cl.createArgument().setFile( privateKey );
235 }
236
237 String password = authenticationInfo.getPassword();
238 if ( putty && password != null )
239 {
240 cl.createArgument().setValue( "-pw" );
241 cl.createArgument().setValue( password );
242 }
243
244
245 if ( putty )
246 {
247 cl.createArgument().setValue( "-batch" );
248 }
249 else
250 {
251 cl.createArgument().setValue( "-o" );
252 cl.createArgument().setValue( "BatchMode yes" );
253 }
254 return cl;
255 }
256
257
258 private void executeScpCommand( Resource resource, File localFile, boolean put )
259 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
260 {
261 boolean putty = isPuTTYSCP();
262
263 File privateKey;
264 try
265 {
266 privateKey = ScpHelper.getPrivateKey( authenticationInfo );
267 }
268 catch ( FileNotFoundException e )
269 {
270 fireSessionConnectionRefused();
271
272 throw new AuthorizationException( e.getMessage() );
273 }
274 Commandline cl = createBaseCommandLine( putty, scpExecutable, privateKey );
275
276 cl.setWorkingDirectory( localFile.getParentFile().getAbsolutePath() );
277
278 int port =
279 repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
280 if ( port != ScpHelper.DEFAULT_SSH_PORT )
281 {
282 cl.createArgument().setLine( "-P " + port );
283 }
284
285 if ( scpArgs != null )
286 {
287 cl.createArgument().setLine( scpArgs );
288 }
289
290 String resourceName = normalizeResource( resource );
291 String remoteFile = getRepository().getBasedir() + "/" + resourceName;
292
293 remoteFile = StringUtils.replace( remoteFile, " ", "\\ " );
294
295 String qualifiedRemoteFile = this.buildRemoteHost() + ":" + remoteFile;
296 if ( put )
297 {
298 cl.createArgument().setValue( localFile.getName() );
299 cl.createArgument().setValue( qualifiedRemoteFile );
300 }
301 else
302 {
303 cl.createArgument().setValue( qualifiedRemoteFile );
304 cl.createArgument().setValue( localFile.getName() );
305 }
306
307 fireSessionDebug( "Executing command: " + cl.toString() );
308
309 try
310 {
311 CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
312 int exitCode = CommandLineUtils.executeCommandLine( cl, null, err );
313 if ( exitCode != 0 )
314 {
315 if ( !put &&
316 err.getOutput().trim().toLowerCase( Locale.ENGLISH ).indexOf( "no such file or directory" ) != -1 )
317 {
318 throw new ResourceDoesNotExistException( err.getOutput() );
319 }
320 else
321 {
322 TransferFailedException e = new TransferFailedException( "Exit code: " + exitCode + " - "
323 + err.getOutput() );
324
325 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
326
327 throw e;
328 }
329 }
330 }
331 catch ( CommandLineException e )
332 {
333 fireTransferError( resource, e, put ? TransferEvent.REQUEST_PUT : TransferEvent.REQUEST_GET );
334
335 throw new TransferFailedException( "Error executing command line", e );
336 }
337 }
338
339 boolean isPuTTYSCP()
340 {
341 return scpExecutable.toLowerCase( Locale.ENGLISH ).indexOf( "pscp" ) >= 0;
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 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 }