1
package org.apache.maven.wagon.providers.ssh;
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
22
import org.apache.sshd.common.util.DirectoryScanner;
23
import org.apache.sshd.server.Command;
24
import org.apache.sshd.server.Environment;
25
import org.apache.sshd.server.ExitCallback;
26
import org.apache.sshd.server.FileSystemAware;
27
import org.apache.sshd.server.FileSystemView;
28
import org.apache.sshd.server.SshFile;
29
import org.slf4j.Logger;
30
import org.slf4j.LoggerFactory;
32
import java.io.ByteArrayOutputStream;
33
import java.io.EOFException;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.OutputStream;
37
import java.util.Arrays;
40
* This commands provide SCP support on both server and client side.
41
* Permissions and preservation of access / modification times on files
43
* olamy : copy of a class from mina for changing return codes in case of file not found 1 warning instead of 2
45
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
47
public class ScpCommand
48
implements Command, Runnable, FileSystemAware
51
protected static final Logger log = LoggerFactory.getLogger( ScpCommand.class );
53
protected static final int OK = 0;
55
protected static final int WARNING = 1;
57
protected static final int ERROR = 2;
59
protected String name;
61
protected boolean optR;
63
protected boolean optT;
65
protected boolean optF;
67
protected boolean optV;
69
protected boolean optD;
71
protected boolean optP;
73
protected FileSystemView root;
75
protected String path;
77
protected InputStream in;
79
protected OutputStream out;
81
protected OutputStream err;
83
protected ExitCallback callback;
85
protected IOException error;
87
public ScpCommand( String[] args )
89
name = Arrays.asList( args ).toString();
90
if ( log.isDebugEnabled() )
92
log.debug( "Executing command {}", name );
95
for ( int i = 1; i < args.length; i++ )
97
if ( args[i].charAt( 0 ) == '-' )
99
for ( int j = 1; j < args[i].length(); j++ )
101
switch ( args[i].charAt( j ) )
122
// error = new IOException("Unsupported option: " + args[i].charAt(j));
127
else if ( i == args.length - 1 )
129
path = args[args.length - 1];
132
if ( !optF && !optT )
134
error = new IOException( "Either -f or -t option should be set" );
138
public void setInputStream( InputStream in )
143
public void setOutputStream( OutputStream out )
148
public void setErrorStream( OutputStream err )
153
public void setExitCallback( ExitCallback callback )
155
this.callback = callback;
158
public void start( Environment env )
165
new Thread( this, "ScpCommand: " + name ).start();
168
public void destroy()
175
String exitMessage = null;
185
boolean isDir = false;
186
int c = readAck( true );
194
line = ( (char) c ) + readLine();
200
//a real ack that has been acted upon already
206
writeDir( line, root.getFile( path ) );
210
writeFile( line, root.getFile( path ) );
216
String pattern = path;
217
int idx = pattern.indexOf( '*' );
221
int lastSep = pattern.substring( 0, idx ).lastIndexOf( '/' );
224
basedir = pattern.substring( 0, lastSep );
225
pattern = pattern.substring( lastSep + 1 );
227
String[] included = new DirectoryScanner( basedir, pattern ).scan();
228
for ( String path : included )
230
SshFile file = root.getFile( basedir + "/" + path );
235
else if ( file.isDirectory() )
239
out.write( WARNING );
240
out.write( ( path + " not a regular file\n" ).getBytes() );
249
out.write( WARNING );
250
out.write( ( path + " unknown file type\n" ).getBytes() );
257
int lastSep = pattern.lastIndexOf( '/' );
260
basedir = pattern.substring( 0, lastSep );
261
pattern = pattern.substring( lastSep + 1 );
263
SshFile file = root.getFile( basedir + "/" + pattern );
264
if ( !file.doesExist() )
267
throw new IOException( file + ": no such file or directory" );
273
else if ( file.isDirectory() )
277
throw new IOException( file + " not a regular file" );
286
throw new IOException( file + ": unknown file type" );
292
throw new IOException( "Unsupported mode" );
295
catch ( IOException e )
299
exitValue = ( exitValue != OK ? exitValue : ERROR );
300
exitMessage = e.getMessage();
301
out.write( exitValue );
302
out.write( exitMessage.getBytes() );
306
catch ( IOException e2 )
310
log.info( "Error in scp command", e );
314
if ( callback != null )
316
callback.onExit( exitValue, exitMessage );
321
protected void writeDir( String header, SshFile path )
324
if ( log.isDebugEnabled() )
326
log.debug( "Writing dir {}", path );
328
if ( !header.startsWith( "D" ) )
330
throw new IOException( "Expected a D message but got '" + header + "'" );
333
String perms = header.substring( 1, 5 );
334
int length = Integer.parseInt( header.substring( 6, header.indexOf( ' ', 6 ) ) );
335
String name = header.substring( header.indexOf( ' ', 6 ) + 1 );
339
throw new IOException( "Expected 0 length for directory but got " + length );
342
if ( path.doesExist() && path.isDirectory() )
344
file = root.getFile( path, name );
346
else if ( !path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory() )
352
throw new IOException( "Can not write to " + path );
354
if ( !( file.doesExist() && file.isDirectory() ) && !file.mkdir() )
356
throw new IOException( "Could not create directory " + file );
364
if ( header.startsWith( "C" ) )
366
writeFile( header, file );
368
else if ( header.startsWith( "D" ) )
370
writeDir( header, file );
372
else if ( header.equals( "E" ) )
379
throw new IOException( "Unexpected message: '" + header + "'" );
385
protected void writeFile( String header, SshFile path )
388
if ( log.isDebugEnabled() )
390
log.debug( "Writing file {}", path );
392
if ( !header.startsWith( "C" ) )
394
throw new IOException( "Expected a C message but got '" + header + "'" );
397
String perms = header.substring( 1, 5 );
398
long length = Long.parseLong( header.substring( 6, header.indexOf( ' ', 6 ) ) );
399
String name = header.substring( header.indexOf( ' ', 6 ) + 1 );
402
if ( path.doesExist() && path.isDirectory() )
404
file = root.getFile( path, name );
406
else if ( path.doesExist() && path.isFile() )
410
else if ( !path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory() )
416
throw new IOException( "Can not write to " + path );
418
if ( file.doesExist() && file.isDirectory() )
420
throw new IOException( "File is a directory: " + file );
422
else if ( file.doesExist() && !file.isWritable() )
424
throw new IOException( "Can not write to file: " + file );
426
OutputStream os = file.createOutputStream( 0 );
431
byte[] buffer = new byte[8192];
434
int len = (int) Math.min( length, buffer.length );
435
len = in.read( buffer, 0, len );
438
throw new IOException( "End of stream reached" );
440
os.write( buffer, 0, len );
453
protected String readLine()
456
ByteArrayOutputStream baos = new ByteArrayOutputStream();
462
return baos.toString();
466
throw new IOException( "End of stream" );
475
protected void readFile( SshFile path )
478
if ( log.isDebugEnabled() )
480
log.debug( "Reading file {}", path );
482
StringBuffer buf = new StringBuffer();
484
buf.append( "0644" ); // what about perms
486
buf.append( path.getSize() ); // length
488
buf.append( path.getName() );
490
out.write( buf.toString().getBytes() );
494
InputStream is = path.createInputStream( 0 );
497
byte[] buffer = new byte[8192];
500
int len = is.read( buffer, 0, buffer.length );
505
out.write( buffer, 0, len );
516
protected void readDir( SshFile path )
519
if ( log.isDebugEnabled() )
521
log.debug( "Reading directory {}", path );
523
StringBuffer buf = new StringBuffer();
525
buf.append( "0755" ); // what about perms
527
buf.append( "0" ); // length
529
buf.append( path.getName() );
531
out.write( buf.toString().getBytes() );
535
for ( SshFile child : path.listSshFiles() )
537
if ( child.isFile() )
541
else if ( child.isDirectory() )
547
out.write( "E\n".getBytes() );
559
protected int readAck( boolean canEof )
568
throw new EOFException();
574
log.warn( "Received warning: " + readLine() );
577
throw new IOException( "Received nack: " + readLine() );
584
public void setFileSystemView( FileSystemView view )