View Javadoc
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.maven.wagon.CommandExecutionException;
23  import org.apache.maven.wagon.CommandExecutor;
24  import org.apache.maven.wagon.PathUtils;
25  import org.apache.maven.wagon.PermissionModeUtils;
26  import org.apache.maven.wagon.ResourceDoesNotExistException;
27  import org.apache.maven.wagon.Streams;
28  import org.apache.maven.wagon.TransferFailedException;
29  import org.apache.maven.wagon.Wagon;
30  import org.apache.maven.wagon.authentication.AuthenticationInfo;
31  import org.apache.maven.wagon.authorization.AuthorizationException;
32  import org.apache.maven.wagon.repository.Repository;
33  import org.apache.maven.wagon.repository.RepositoryPermissions;
34  import org.apache.maven.wagon.resource.Resource;
35  import org.codehaus.plexus.util.FileUtils;
36  import org.codehaus.plexus.util.IOUtil;
37  import org.codehaus.plexus.util.StringUtils;
38  
39  import java.io.File;
40  import java.io.FileInputStream;
41  import java.io.FileNotFoundException;
42  import java.io.FileOutputStream;
43  import java.io.IOException;
44  import java.util.List;
45  import java.util.zip.ZipEntry;
46  import java.util.zip.ZipOutputStream;
47  
48  /**
49   * Scp helper for general algorithms on ssh server.
50   * See {@link #putDirectory(Wagon, File, String) putDirectory(...)} for more info on bulk directory upload.
51   */
52  public class ScpHelper
53  {
54      public static final char PATH_SEPARATOR = '/';
55  
56      public static final int DEFAULT_SSH_PORT = 22;
57  
58      private final CommandExecutor executor;
59  
60      public ScpHelper( CommandExecutor executor )
61      {
62          this.executor = executor;
63      }
64  
65      public static String getResourceDirectory( String resourceName )
66      {
67          String dir = PathUtils.dirname( resourceName );
68          dir = StringUtils.replace( dir, "\\", "/" );
69          return dir;
70      }
71  
72      public static String getResourceFilename( String r )
73      {
74          String filename;
75          if ( r.lastIndexOf( PATH_SEPARATOR ) > 0 )
76          {
77              filename = r.substring( r.lastIndexOf( PATH_SEPARATOR ) + 1 );
78          }
79          else
80          {
81              filename = r;
82          }
83          return filename;
84      }
85  
86      public static Resource getResource( String resourceName )
87      {
88          String r = StringUtils.replace( resourceName, "\\", "/" );
89          return new Resource( r );
90      }
91  
92      public static File getPrivateKey( AuthenticationInfo authenticationInfo )
93          throws FileNotFoundException
94      {
95          // If user don't define a password, he want to use a private key
96          File privateKey = null;
97          if ( authenticationInfo.getPassword() == null )
98          {
99  
100             if ( authenticationInfo.getPrivateKey() != null )
101             {
102                 privateKey = new File( authenticationInfo.getPrivateKey() );
103                 if ( !privateKey.exists() )
104                 {
105                     throw new FileNotFoundException( "Private key '" + privateKey + "' not found" );
106                 }
107             }
108             else
109             {
110                 privateKey = findPrivateKey();
111             }
112 
113             if ( privateKey != null && privateKey.exists() )
114             {
115                 if ( authenticationInfo.getPassphrase() == null )
116                 {
117                     authenticationInfo.setPassphrase( "" );
118                 }
119             }
120         }
121         return privateKey;
122     }
123 
124     private static File findPrivateKey()
125     {
126         String privateKeyDirectory = System.getProperty( "wagon.privateKeyDirectory" );
127 
128         if ( privateKeyDirectory == null )
129         {
130             privateKeyDirectory = System.getProperty( "user.home" );
131         }
132 
133         File privateKey = new File( privateKeyDirectory, ".ssh/id_dsa" );
134 
135         if ( !privateKey.exists() )
136         {
137             privateKey = new File( privateKeyDirectory, ".ssh/id_rsa" );
138             if ( !privateKey.exists() )
139             {
140                 privateKey = null;
141             }
142         }
143 
144         return privateKey;
145     }
146 
147     public static void createZip( List<String> files, File zipName, File basedir )
148         throws IOException
149     {
150         ZipOutputStream zos = null;
151         try
152         {
153             zos = new ZipOutputStream( new FileOutputStream( zipName ) );
154 
155             for ( String file : files )
156             {
157                 file = file.replace( '\\', '/' );
158 
159                 writeZipEntry( zos, new File( basedir, file ), file );
160             }
161 
162             zos.close();
163             zos = null;
164         }
165         finally
166         {
167             IOUtil.close( zos );
168         }
169     }
170 
171     private static void writeZipEntry( ZipOutputStream jar, File source, String entryName )
172         throws IOException
173     {
174         byte[] buffer = new byte[1024];
175 
176         int bytesRead;
177 
178         FileInputStream is = new FileInputStream( source );
179 
180         try
181         {
182             ZipEntry entry = new ZipEntry( entryName );
183 
184             jar.putNextEntry( entry );
185 
186             while ( ( bytesRead = is.read( buffer ) ) != -1 )
187             {
188                 jar.write( buffer, 0, bytesRead );
189             }
190         }
191         finally
192         {
193             is.close();
194         }
195     }
196 
197     protected static String getPath( String basedir, String dir )
198     {
199         String path;
200         path = basedir;
201         if ( !basedir.endsWith( "/" ) && !dir.startsWith( "/" ) )
202         {
203             path += "/";
204         }
205         path += dir;
206         return path;
207     }
208 
209     /**
210      * Put a whole directory content, by transferring a unique zip file and uncompressing it
211      * on the target ssh server with <code>unzip</code> command.
212      */
213     public void putDirectory( Wagon wagon, File sourceDirectory, String destinationDirectory )
214         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
215     {
216         Repository repository = wagon.getRepository();
217 
218         String basedir = repository.getBasedir();
219 
220         String destDir = StringUtils.replace( destinationDirectory, "\\", "/" );
221 
222         String path = getPath( basedir, destDir );
223         try
224         {
225             if ( repository.getPermissions() != null )
226             {
227                 String dirPerms = repository.getPermissions().getDirectoryMode();
228 
229                 if ( dirPerms != null )
230                 {
231                     String umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
232                     executor.executeCommand( umaskCmd );
233                 }
234             }
235 
236             //String mkdirCmd = "mkdir -p " + path;
237             String mkdirCmd = "mkdir -p \"" + path + "\"";
238 
239             executor.executeCommand( mkdirCmd );
240         }
241         catch ( CommandExecutionException e )
242         {
243             throw new TransferFailedException( "Error performing commands for file transfer", e );
244         }
245 
246         File zipFile;
247         try
248         {
249             zipFile = File.createTempFile( "wagon", ".zip" );
250             zipFile.deleteOnExit();
251 
252             List<String> files = FileUtils.getFileNames( sourceDirectory, "**/**", "", false );
253 
254             createZip( files, zipFile, sourceDirectory );
255         }
256         catch ( IOException e )
257         {
258             throw new TransferFailedException( "Unable to create ZIP archive of directory", e );
259         }
260 
261         wagon.put( zipFile, getPath( destDir, zipFile.getName() ) );
262 
263         try
264         {
265             //executor.executeCommand(
266             //    "cd " + path + "; unzip -q -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );
267             executor.executeCommand( "cd \"" + path + "\"; unzip -q -o \"" + zipFile.getName() + "\"; rm -f \""
268                 + zipFile.getName() + "\"" );
269 
270             zipFile.delete();
271 
272             RepositoryPermissions permissions = repository.getPermissions();
273 
274             if ( permissions != null && permissions.getGroup() != null )
275             {
276                 //executor.executeCommand( "chgrp -Rf " + permissions.getGroup() + " " + path );
277                 executor.executeCommand( "chgrp -Rf " + permissions.getGroup() + " \"" + path + "\"" );
278             }
279 
280             if ( permissions != null && permissions.getFileMode() != null )
281             {
282                 //executor.executeCommand( "chmod -Rf " + permissions.getFileMode() + " " + path );
283                 executor.executeCommand( "chmod -Rf " + permissions.getFileMode() + " \"" + path + "\"" );
284             }
285         }
286         catch ( CommandExecutionException e )
287         {
288             throw new TransferFailedException( "Error performing commands for file transfer", e );
289         }
290     }
291 
292     public List<String> getFileList( String destinationDirectory, Repository repository )
293         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
294     {
295         try
296         {
297             String path = getPath( repository.getBasedir(), destinationDirectory );
298             //Streams streams = executor.executeCommand( "ls -FlA " + path, false );
299             Streams streams = executor.executeCommand( "ls -FlA \"" + path + "\"", false );
300 
301             return new LSParser().parseFiles( streams.getOut() );
302         }
303         catch ( CommandExecutionException e )
304         {
305             if ( e.getMessage().trim().endsWith( "No such file or directory" ) )
306             {
307                 throw new ResourceDoesNotExistException( e.getMessage().trim(), e );
308             }
309             else if ( e.getMessage().trim().endsWith( "Not a directory" ) )
310             {
311                 throw new ResourceDoesNotExistException( e.getMessage().trim(), e );
312             }
313             else
314             {
315                 throw new TransferFailedException( "Error performing file listing.", e );
316             }
317         }
318     }
319 
320     public boolean resourceExists( String resourceName, Repository repository )
321         throws TransferFailedException, AuthorizationException
322     {
323         try
324         {
325             String path = getPath( repository.getBasedir(), resourceName );
326             //executor.executeCommand( "ls " + path, false );
327             executor.executeCommand( "ls \"" + path + "\"" );
328 
329             // Parsing of output not really needed.  As a failed ls results in a
330             // CommandExectionException on the 'ls' command.
331 
332             return true;
333         }
334         catch ( CommandExecutionException e )
335         {
336             // Error?  Then the 'ls' command failed.  No such file found.
337             return false;
338         }
339     }
340 
341     public void createRemoteDirectories( String path, RepositoryPermissions permissions )
342         throws CommandExecutionException
343     {
344         String umaskCmd = null;
345         if ( permissions != null )
346         {
347             String dirPerms = permissions.getDirectoryMode();
348 
349             if ( dirPerms != null )
350             {
351                 umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
352             }
353         }
354 
355         //String mkdirCmd = "mkdir -p " + path;
356         String mkdirCmd = "mkdir -p \"" + path + "\"";
357 
358         if ( umaskCmd != null )
359         {
360             mkdirCmd = umaskCmd + "; " + mkdirCmd;
361         }
362 
363         executor.executeCommand( mkdirCmd );
364     }
365 }