~ubuntu-branches/ubuntu/raring/wagon2/raring-proposed

« back to all changes in this revision

Viewing changes to wagon-providers/wagon-ssh-common-test/src/main/java/org/apache/maven/wagon/providers/ssh/ScpCommand.java

  • Committer: Package Import Robot
  • Author(s): Damien Raude-Morvan
  • Date: 2012-01-29 23:23:22 UTC
  • Revision ID: package-import@ubuntu.com-20120129232322-w9h5j9c81zi8f23o
Tags: upstream-2.2
ImportĀ upstreamĀ versionĀ 2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.apache.maven.wagon.providers.ssh;
 
2
 
 
3
/*
 
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
 
11
 *
 
12
 *   http://www.apache.org/licenses/LICENSE-2.0
 
13
 *
 
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
 
19
 * under the License.
 
20
 */
 
21
 
 
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;
 
31
 
 
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;
 
38
 
 
39
/**
 
40
 * This commands provide SCP support on both server and client side.
 
41
 * Permissions and preservation of access / modification times on files
 
42
 * are not supported.
 
43
 * olamy : copy of a class from mina for changing return codes in case of file not found 1 warning instead of 2
 
44
 *
 
45
 * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
 
46
 */
 
47
public class ScpCommand
 
48
    implements Command, Runnable, FileSystemAware
 
49
{
 
50
 
 
51
    protected static final Logger log = LoggerFactory.getLogger( ScpCommand.class );
 
52
 
 
53
    protected static final int OK = 0;
 
54
 
 
55
    protected static final int WARNING = 1;
 
56
 
 
57
    protected static final int ERROR = 2;
 
58
 
 
59
    protected String name;
 
60
 
 
61
    protected boolean optR;
 
62
 
 
63
    protected boolean optT;
 
64
 
 
65
    protected boolean optF;
 
66
 
 
67
    protected boolean optV;
 
68
 
 
69
    protected boolean optD;
 
70
 
 
71
    protected boolean optP;
 
72
 
 
73
    protected FileSystemView root;
 
74
 
 
75
    protected String path;
 
76
 
 
77
    protected InputStream in;
 
78
 
 
79
    protected OutputStream out;
 
80
 
 
81
    protected OutputStream err;
 
82
 
 
83
    protected ExitCallback callback;
 
84
 
 
85
    protected IOException error;
 
86
 
 
87
    public ScpCommand( String[] args )
 
88
    {
 
89
        name = Arrays.asList( args ).toString();
 
90
        if ( log.isDebugEnabled() )
 
91
        {
 
92
            log.debug( "Executing command {}", name );
 
93
        }
 
94
        path = ".";
 
95
        for ( int i = 1; i < args.length; i++ )
 
96
        {
 
97
            if ( args[i].charAt( 0 ) == '-' )
 
98
            {
 
99
                for ( int j = 1; j < args[i].length(); j++ )
 
100
                {
 
101
                    switch ( args[i].charAt( j ) )
 
102
                    {
 
103
                        case 'f':
 
104
                            optF = true;
 
105
                            break;
 
106
                        case 'p':
 
107
                            optP = true;
 
108
                            break;
 
109
                        case 'r':
 
110
                            optR = true;
 
111
                            break;
 
112
                        case 't':
 
113
                            optT = true;
 
114
                            break;
 
115
                        case 'v':
 
116
                            optV = true;
 
117
                            break;
 
118
                        case 'd':
 
119
                            optD = true;
 
120
                            break;
 
121
//                          default:
 
122
//                            error = new IOException("Unsupported option: " + args[i].charAt(j));
 
123
//                            return;
 
124
                    }
 
125
                }
 
126
            }
 
127
            else if ( i == args.length - 1 )
 
128
            {
 
129
                path = args[args.length - 1];
 
130
            }
 
131
        }
 
132
        if ( !optF && !optT )
 
133
        {
 
134
            error = new IOException( "Either -f or -t option should be set" );
 
135
        }
 
136
    }
 
137
 
 
138
    public void setInputStream( InputStream in )
 
139
    {
 
140
        this.in = in;
 
141
    }
 
142
 
 
143
    public void setOutputStream( OutputStream out )
 
144
    {
 
145
        this.out = out;
 
146
    }
 
147
 
 
148
    public void setErrorStream( OutputStream err )
 
149
    {
 
150
        this.err = err;
 
151
    }
 
152
 
 
153
    public void setExitCallback( ExitCallback callback )
 
154
    {
 
155
        this.callback = callback;
 
156
    }
 
157
 
 
158
    public void start( Environment env )
 
159
        throws IOException
 
160
    {
 
161
        if ( error != null )
 
162
        {
 
163
            throw error;
 
164
        }
 
165
        new Thread( this, "ScpCommand: " + name ).start();
 
166
    }
 
167
 
 
168
    public void destroy()
 
169
    {
 
170
    }
 
171
 
 
172
    public void run()
 
173
    {
 
174
        int exitValue = OK;
 
175
        String exitMessage = null;
 
176
 
 
177
        try
 
178
        {
 
179
            if ( optT )
 
180
            {
 
181
                ack();
 
182
                for (; ; )
 
183
                {
 
184
                    String line;
 
185
                    boolean isDir = false;
 
186
                    int c = readAck( true );
 
187
                    switch ( c )
 
188
                    {
 
189
                        case -1:
 
190
                            return;
 
191
                        case 'D':
 
192
                            isDir = true;
 
193
                        case 'C':
 
194
                            line = ( (char) c ) + readLine();
 
195
                            break;
 
196
                        case 'E':
 
197
                            readLine();
 
198
                            return;
 
199
                        default:
 
200
                            //a real ack that has been acted upon already
 
201
                            continue;
 
202
                    }
 
203
 
 
204
                    if ( optR && isDir )
 
205
                    {
 
206
                        writeDir( line, root.getFile( path ) );
 
207
                    }
 
208
                    else
 
209
                    {
 
210
                        writeFile( line, root.getFile( path ) );
 
211
                    }
 
212
                }
 
213
            }
 
214
            else if ( optF )
 
215
            {
 
216
                String pattern = path;
 
217
                int idx = pattern.indexOf( '*' );
 
218
                if ( idx >= 0 )
 
219
                {
 
220
                    String basedir = "";
 
221
                    int lastSep = pattern.substring( 0, idx ).lastIndexOf( '/' );
 
222
                    if ( lastSep >= 0 )
 
223
                    {
 
224
                        basedir = pattern.substring( 0, lastSep );
 
225
                        pattern = pattern.substring( lastSep + 1 );
 
226
                    }
 
227
                    String[] included = new DirectoryScanner( basedir, pattern ).scan();
 
228
                    for ( String path : included )
 
229
                    {
 
230
                        SshFile file = root.getFile( basedir + "/" + path );
 
231
                        if ( file.isFile() )
 
232
                        {
 
233
                            readFile( file );
 
234
                        }
 
235
                        else if ( file.isDirectory() )
 
236
                        {
 
237
                            if ( !optR )
 
238
                            {
 
239
                                out.write( WARNING );
 
240
                                out.write( ( path + " not a regular file\n" ).getBytes() );
 
241
                            }
 
242
                            else
 
243
                            {
 
244
                                readDir( file );
 
245
                            }
 
246
                        }
 
247
                        else
 
248
                        {
 
249
                            out.write( WARNING );
 
250
                            out.write( ( path + " unknown file type\n" ).getBytes() );
 
251
                        }
 
252
                    }
 
253
                }
 
254
                else
 
255
                {
 
256
                    String basedir = "";
 
257
                    int lastSep = pattern.lastIndexOf( '/' );
 
258
                    if ( lastSep >= 0 )
 
259
                    {
 
260
                        basedir = pattern.substring( 0, lastSep );
 
261
                        pattern = pattern.substring( lastSep + 1 );
 
262
                    }
 
263
                    SshFile file = root.getFile( basedir + "/" + pattern );
 
264
                    if ( !file.doesExist() )
 
265
                    {
 
266
                        exitValue = WARNING;
 
267
                        throw new IOException( file + ": no such file or directory" );
 
268
                    }
 
269
                    if ( file.isFile() )
 
270
                    {
 
271
                        readFile( file );
 
272
                    }
 
273
                    else if ( file.isDirectory() )
 
274
                    {
 
275
                        if ( !optR )
 
276
                        {
 
277
                            throw new IOException( file + " not a regular file" );
 
278
                        }
 
279
                        else
 
280
                        {
 
281
                            readDir( file );
 
282
                        }
 
283
                    }
 
284
                    else
 
285
                    {
 
286
                        throw new IOException( file + ": unknown file type" );
 
287
                    }
 
288
                }
 
289
            }
 
290
            else
 
291
            {
 
292
                throw new IOException( "Unsupported mode" );
 
293
            }
 
294
        }
 
295
        catch ( IOException e )
 
296
        {
 
297
            try
 
298
            {
 
299
                exitValue = ( exitValue != OK ? exitValue : ERROR );
 
300
                exitMessage = e.getMessage();
 
301
                out.write( exitValue );
 
302
                out.write( exitMessage.getBytes() );
 
303
                out.write( '\n' );
 
304
                out.flush();
 
305
            }
 
306
            catch ( IOException e2 )
 
307
            {
 
308
                // Ignore
 
309
            }
 
310
            log.info( "Error in scp command", e );
 
311
        }
 
312
        finally
 
313
        {
 
314
            if ( callback != null )
 
315
            {
 
316
                callback.onExit( exitValue, exitMessage );
 
317
            }
 
318
        }
 
319
    }
 
320
 
 
321
    protected void writeDir( String header, SshFile path )
 
322
        throws IOException
 
323
    {
 
324
        if ( log.isDebugEnabled() )
 
325
        {
 
326
            log.debug( "Writing dir {}", path );
 
327
        }
 
328
        if ( !header.startsWith( "D" ) )
 
329
        {
 
330
            throw new IOException( "Expected a D message but got '" + header + "'" );
 
331
        }
 
332
 
 
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 );
 
336
 
 
337
        if ( length != 0 )
 
338
        {
 
339
            throw new IOException( "Expected 0 length for directory but got " + length );
 
340
        }
 
341
        SshFile file;
 
342
        if ( path.doesExist() && path.isDirectory() )
 
343
        {
 
344
            file = root.getFile( path, name );
 
345
        }
 
346
        else if ( !path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory() )
 
347
        {
 
348
            file = path;
 
349
        }
 
350
        else
 
351
        {
 
352
            throw new IOException( "Can not write to " + path );
 
353
        }
 
354
        if ( !( file.doesExist() && file.isDirectory() ) && !file.mkdir() )
 
355
        {
 
356
            throw new IOException( "Could not create directory " + file );
 
357
        }
 
358
 
 
359
        ack();
 
360
 
 
361
        for (; ; )
 
362
        {
 
363
            header = readLine();
 
364
            if ( header.startsWith( "C" ) )
 
365
            {
 
366
                writeFile( header, file );
 
367
            }
 
368
            else if ( header.startsWith( "D" ) )
 
369
            {
 
370
                writeDir( header, file );
 
371
            }
 
372
            else if ( header.equals( "E" ) )
 
373
            {
 
374
                ack();
 
375
                break;
 
376
            }
 
377
            else
 
378
            {
 
379
                throw new IOException( "Unexpected message: '" + header + "'" );
 
380
            }
 
381
        }
 
382
 
 
383
    }
 
384
 
 
385
    protected void writeFile( String header, SshFile path )
 
386
        throws IOException
 
387
    {
 
388
        if ( log.isDebugEnabled() )
 
389
        {
 
390
            log.debug( "Writing file {}", path );
 
391
        }
 
392
        if ( !header.startsWith( "C" ) )
 
393
        {
 
394
            throw new IOException( "Expected a C message but got '" + header + "'" );
 
395
        }
 
396
 
 
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 );
 
400
 
 
401
        SshFile file;
 
402
        if ( path.doesExist() && path.isDirectory() )
 
403
        {
 
404
            file = root.getFile( path, name );
 
405
        }
 
406
        else if ( path.doesExist() && path.isFile() )
 
407
        {
 
408
            file = path;
 
409
        }
 
410
        else if ( !path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory() )
 
411
        {
 
412
            file = path;
 
413
        }
 
414
        else
 
415
        {
 
416
            throw new IOException( "Can not write to " + path );
 
417
        }
 
418
        if ( file.doesExist() && file.isDirectory() )
 
419
        {
 
420
            throw new IOException( "File is a directory: " + file );
 
421
        }
 
422
        else if ( file.doesExist() && !file.isWritable() )
 
423
        {
 
424
            throw new IOException( "Can not write to file: " + file );
 
425
        }
 
426
        OutputStream os = file.createOutputStream( 0 );
 
427
        try
 
428
        {
 
429
            ack();
 
430
 
 
431
            byte[] buffer = new byte[8192];
 
432
            while ( length > 0 )
 
433
            {
 
434
                int len = (int) Math.min( length, buffer.length );
 
435
                len = in.read( buffer, 0, len );
 
436
                if ( len <= 0 )
 
437
                {
 
438
                    throw new IOException( "End of stream reached" );
 
439
                }
 
440
                os.write( buffer, 0, len );
 
441
                length -= len;
 
442
            }
 
443
        }
 
444
        finally
 
445
        {
 
446
            os.close();
 
447
        }
 
448
 
 
449
        ack();
 
450
        readAck( false );
 
451
    }
 
452
 
 
453
    protected String readLine()
 
454
        throws IOException
 
455
    {
 
456
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
457
        for (; ; )
 
458
        {
 
459
            int c = in.read();
 
460
            if ( c == '\n' )
 
461
            {
 
462
                return baos.toString();
 
463
            }
 
464
            else if ( c == -1 )
 
465
            {
 
466
                throw new IOException( "End of stream" );
 
467
            }
 
468
            else
 
469
            {
 
470
                baos.write( c );
 
471
            }
 
472
        }
 
473
    }
 
474
 
 
475
    protected void readFile( SshFile path )
 
476
        throws IOException
 
477
    {
 
478
        if ( log.isDebugEnabled() )
 
479
        {
 
480
            log.debug( "Reading file {}", path );
 
481
        }
 
482
        StringBuffer buf = new StringBuffer();
 
483
        buf.append( "C" );
 
484
        buf.append( "0644" ); // what about perms
 
485
        buf.append( " " );
 
486
        buf.append( path.getSize() ); // length
 
487
        buf.append( " " );
 
488
        buf.append( path.getName() );
 
489
        buf.append( "\n" );
 
490
        out.write( buf.toString().getBytes() );
 
491
        out.flush();
 
492
        readAck( false );
 
493
 
 
494
        InputStream is = path.createInputStream( 0 );
 
495
        try
 
496
        {
 
497
            byte[] buffer = new byte[8192];
 
498
            for (; ; )
 
499
            {
 
500
                int len = is.read( buffer, 0, buffer.length );
 
501
                if ( len == -1 )
 
502
                {
 
503
                    break;
 
504
                }
 
505
                out.write( buffer, 0, len );
 
506
            }
 
507
        }
 
508
        finally
 
509
        {
 
510
            is.close();
 
511
        }
 
512
        ack();
 
513
        readAck( false );
 
514
    }
 
515
 
 
516
    protected void readDir( SshFile path )
 
517
        throws IOException
 
518
    {
 
519
        if ( log.isDebugEnabled() )
 
520
        {
 
521
            log.debug( "Reading directory {}", path );
 
522
        }
 
523
        StringBuffer buf = new StringBuffer();
 
524
        buf.append( "D" );
 
525
        buf.append( "0755" ); // what about perms
 
526
        buf.append( " " );
 
527
        buf.append( "0" ); // length
 
528
        buf.append( " " );
 
529
        buf.append( path.getName() );
 
530
        buf.append( "\n" );
 
531
        out.write( buf.toString().getBytes() );
 
532
        out.flush();
 
533
        readAck( false );
 
534
 
 
535
        for ( SshFile child : path.listSshFiles() )
 
536
        {
 
537
            if ( child.isFile() )
 
538
            {
 
539
                readFile( child );
 
540
            }
 
541
            else if ( child.isDirectory() )
 
542
            {
 
543
                readDir( child );
 
544
            }
 
545
        }
 
546
 
 
547
        out.write( "E\n".getBytes() );
 
548
        out.flush();
 
549
        readAck( false );
 
550
    }
 
551
 
 
552
    protected void ack()
 
553
        throws IOException
 
554
    {
 
555
        out.write( 0 );
 
556
        out.flush();
 
557
    }
 
558
 
 
559
    protected int readAck( boolean canEof )
 
560
        throws IOException
 
561
    {
 
562
        int c = in.read();
 
563
        switch ( c )
 
564
        {
 
565
            case -1:
 
566
                if ( !canEof )
 
567
                {
 
568
                    throw new EOFException();
 
569
                }
 
570
                break;
 
571
            case OK:
 
572
                break;
 
573
            case WARNING:
 
574
                log.warn( "Received warning: " + readLine() );
 
575
                break;
 
576
            case ERROR:
 
577
                throw new IOException( "Received nack: " + readLine() );
 
578
            default:
 
579
                break;
 
580
        }
 
581
        return c;
 
582
    }
 
583
 
 
584
    public void setFileSystemView( FileSystemView view )
 
585
    {
 
586
        this.root = view;
 
587
    }
 
588
}