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