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 = new ZipOutputStream( new FileOutputStream( zipName ) );
151 
152         try
153         {
154             for ( String file : files )
155             {
156                 file = file.replace( '\\', '/' );
157 
158                 writeZipEntry( zos, new File( basedir, file ), file );
159             }
160         }
161         finally
162         {
163             IOUtil.close( zos );
164         }
165     }
166 
167     private static void writeZipEntry( ZipOutputStream jar, File source, String entryName )
168         throws IOException
169     {
170         byte[] buffer = new byte[1024];
171 
172         int bytesRead;
173 
174         FileInputStream is = new FileInputStream( source );
175 
176         try
177         {
178             ZipEntry entry = new ZipEntry( entryName );
179 
180             jar.putNextEntry( entry );
181 
182             while ( ( bytesRead = is.read( buffer ) ) != -1 )
183             {
184                 jar.write( buffer, 0, bytesRead );
185             }
186         }
187         finally
188         {
189             is.close();
190         }
191     }
192 
193     protected static String getPath( String basedir, String dir )
194     {
195         String path;
196         path = basedir;
197         if ( !basedir.endsWith( "/" ) && !dir.startsWith( "/" ) )
198         {
199             path += "/";
200         }
201         path += dir;
202         return path;
203     }
204 
205     /**
206      * Put a whole directory content, by transferring a unique zip file and uncompressing it
207      * on the target ssh server with <code>unzip</code> command.
208      */
209     public void putDirectory( Wagon wagon, File sourceDirectory, String destinationDirectory )
210         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
211     {
212         Repository repository = wagon.getRepository();
213 
214         String basedir = repository.getBasedir();
215 
216         String destDir = StringUtils.replace( destinationDirectory, "\\", "/" );
217 
218         String path = getPath( basedir, destDir );
219         try
220         {
221             if ( repository.getPermissions() != null )
222             {
223                 String dirPerms = repository.getPermissions().getDirectoryMode();
224 
225                 if ( dirPerms != null )
226                 {
227                     String umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
228                     executor.executeCommand( umaskCmd );
229                 }
230             }
231 
232             //String mkdirCmd = "mkdir -p " + path;
233             String mkdirCmd = "mkdir -p \"" + path + "\"";
234 
235             executor.executeCommand( mkdirCmd );
236         }
237         catch ( CommandExecutionException e )
238         {
239             throw new TransferFailedException( "Error performing commands for file transfer", e );
240         }
241 
242         File zipFile;
243         try
244         {
245             zipFile = File.createTempFile( "wagon", ".zip" );
246             zipFile.deleteOnExit();
247 
248             List<String> files = FileUtils.getFileNames( sourceDirectory, "**/**", "", false );
249 
250             createZip( files, zipFile, sourceDirectory );
251         }
252         catch ( IOException e )
253         {
254             throw new TransferFailedException( "Unable to create ZIP archive of directory", e );
255         }
256 
257         wagon.put( zipFile, getPath( destDir, zipFile.getName() ) );
258 
259         try
260         {
261             //executor.executeCommand(
262             //    "cd " + path + "; unzip -q -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );
263             executor.executeCommand( "cd \"" + path + "\"; unzip -q -o \"" + zipFile.getName() + "\"; rm -f \"" + zipFile.getName() + "\"" );
264 
265             zipFile.delete();
266 
267             RepositoryPermissions permissions = repository.getPermissions();
268 
269             if ( permissions != null && permissions.getGroup() != null )
270             {
271                 //executor.executeCommand( "chgrp -Rf " + permissions.getGroup() + " " + path );
272                 executor.executeCommand( "chgrp -Rf " + permissions.getGroup() + " \"" + path + "\"" );
273             }
274 
275             if ( permissions != null && permissions.getFileMode() != null )
276             {
277                 //executor.executeCommand( "chmod -Rf " + permissions.getFileMode() + " " + path );
278                 executor.executeCommand( "chmod -Rf " + permissions.getFileMode() + " \"" + path + "\"" );
279             }
280         }
281         catch ( CommandExecutionException e )
282         {
283             throw new TransferFailedException( "Error performing commands for file transfer", e );
284         }
285     }
286 
287     public List<String> getFileList( String destinationDirectory, Repository repository )
288         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
289     {
290         try
291         {
292             String path = getPath( repository.getBasedir(), destinationDirectory );
293             //Streams streams = executor.executeCommand( "ls -FlA " + path, false );
294             Streams streams = executor.executeCommand( "ls -FlA \"" + path + "\"", false );
295 
296             List<String> ret = new LSParser().parseFiles( streams.getOut() );
297             if ( ret == null || ret.isEmpty() )
298             {
299                 throw new ResourceDoesNotExistException( "No such file or directory" );
300             }
301             return ret;
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 }