View Javadoc
1   /**
2    *
3    * Copyright 2004 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.codehaus.plexus.archiver;
18  
19  import java.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.util.ArrayList;
26  import java.util.Date;
27  import java.util.List;
28  import org.codehaus.plexus.archiver.util.ArchiveEntryUtils;
29  import org.codehaus.plexus.components.io.attributes.SymlinkUtils;
30  import org.codehaus.plexus.components.io.filemappers.FileMapper;
31  import org.codehaus.plexus.components.io.fileselectors.FileSelector;
32  import org.codehaus.plexus.components.io.resources.PlexusIoResource;
33  import org.codehaus.plexus.logging.AbstractLogEnabled;
34  import org.codehaus.plexus.util.FileUtils;
35  import org.codehaus.plexus.util.IOUtil;
36  import org.codehaus.plexus.util.StringUtils;
37  
38  // TODO there should really be constructors which take the source file.
39  
40  /**
41   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
42   */
43  public abstract class AbstractUnArchiver
44      extends AbstractLogEnabled
45      implements UnArchiver, FinalizerEnabled
46  {
47  
48      private File destDirectory;
49  
50      private File destFile;
51  
52      private File sourceFile;
53  
54      private boolean overwrite = true;
55  
56      private FileMapper[] fileMappers;
57  
58      private List finalizers;
59  
60      private FileSelector[] fileSelectors;
61  
62      /**
63       * since 2.3 is on by default
64       *
65       * @since 1.1
66       */
67      private boolean useJvmChmod = true;
68  
69      /**
70       * @since 1.1
71       */
72      private boolean ignorePermissions = false;
73  
74      public AbstractUnArchiver()
75      {
76          // no op
77      }
78  
79      public AbstractUnArchiver( final File sourceFile )
80      {
81          this.sourceFile = sourceFile;
82      }
83  
84      @Override
85      public File getDestDirectory()
86      {
87          return destDirectory;
88      }
89  
90      @Override
91      public void setDestDirectory( final File destDirectory )
92      {
93          this.destDirectory = destDirectory;
94      }
95  
96      @Override
97      public File getDestFile()
98      {
99          return destFile;
100     }
101 
102     @Override
103     public void setDestFile( final File destFile )
104     {
105         this.destFile = destFile;
106     }
107 
108     @Override
109     public File getSourceFile()
110     {
111         return sourceFile;
112     }
113 
114     @Override
115     public void setSourceFile( final File sourceFile )
116     {
117         this.sourceFile = sourceFile;
118     }
119 
120     @Override
121     public boolean isOverwrite()
122     {
123         return overwrite;
124     }
125 
126     @Override
127     public void setOverwrite( final boolean b )
128     {
129         overwrite = b;
130     }
131 
132     @Override
133     public FileMapper[] getFileMappers()
134     {
135         return fileMappers;
136     }
137 
138     @Override
139     public void setFileMappers( final FileMapper[] fileMappers )
140     {
141         this.fileMappers = fileMappers;
142     }
143 
144     @Override
145     public final void extract()
146         throws ArchiverException
147     {
148         validate();
149         execute();
150         runArchiveFinalizers();
151     }
152 
153     @Override
154     public final void extract( final String path, final File outputDirectory )
155         throws ArchiverException
156     {
157         validate( path, outputDirectory );
158         execute( path, outputDirectory );
159         runArchiveFinalizers();
160     }
161 
162     @Override
163     public void addArchiveFinalizer( final ArchiveFinalizer finalizer )
164     {
165         if ( finalizers == null )
166         {
167             finalizers = new ArrayList();
168         }
169 
170         finalizers.add( finalizer );
171     }
172 
173     @Override
174     public void setArchiveFinalizers( final List archiveFinalizers )
175     {
176         finalizers = archiveFinalizers;
177     }
178 
179     private void runArchiveFinalizers()
180         throws ArchiverException
181     {
182         if ( finalizers != null )
183         {
184             for ( Object finalizer1 : finalizers )
185             {
186                 final ArchiveFinalizer finalizer = (ArchiveFinalizer) finalizer1;
187 
188                 finalizer.finalizeArchiveExtraction( this );
189             }
190         }
191     }
192 
193     protected void validate( final String path, final File outputDirectory )
194     {
195     }
196 
197     protected void validate()
198         throws ArchiverException
199     {
200         if ( sourceFile == null )
201         {
202             throw new ArchiverException( "The source file isn't defined." );
203         }
204 
205         if ( sourceFile.isDirectory() )
206         {
207             throw new ArchiverException( "The source must not be a directory." );
208         }
209 
210         if ( !sourceFile.exists() )
211         {
212             throw new ArchiverException( "The source file " + sourceFile + " doesn't exist." );
213         }
214 
215         if ( destDirectory == null && destFile == null )
216         {
217             throw new ArchiverException( "The destination isn't defined." );
218         }
219 
220         if ( destDirectory != null && destFile != null )
221         {
222             throw new ArchiverException( "You must choose between a destination directory and a destination file." );
223         }
224 
225         if ( destDirectory != null && !destDirectory.isDirectory() )
226         {
227             destFile = destDirectory;
228             destDirectory = null;
229         }
230 
231         if ( destFile != null && destFile.isDirectory() )
232         {
233             destDirectory = destFile;
234             destFile = null;
235         }
236     }
237 
238     @Override
239     public void setFileSelectors( final FileSelector[] fileSelectors )
240     {
241         this.fileSelectors = fileSelectors;
242     }
243 
244     @Override
245     public FileSelector[] getFileSelectors()
246     {
247         return fileSelectors;
248     }
249 
250     protected boolean isSelected( final String fileName, final PlexusIoResource fileInfo )
251         throws ArchiverException
252     {
253         if ( fileSelectors != null )
254         {
255             for ( FileSelector fileSelector : fileSelectors )
256             {
257                 try
258                 {
259 
260                     if ( !fileSelector.isSelected( fileInfo ) )
261                     {
262                         return false;
263                     }
264                 }
265                 catch ( final IOException e )
266                 {
267                     throw new ArchiverException(
268                         "Failed to check, whether " + fileInfo.getName() + " is selected: " + e.getMessage(), e );
269                 }
270             }
271         }
272         return true;
273     }
274 
275     protected abstract void execute()
276         throws ArchiverException;
277 
278     protected abstract void execute( String path, File outputDirectory )
279         throws ArchiverException;
280 
281     /**
282      * @since 1.1
283      */
284     @Override
285     public boolean isUseJvmChmod()
286     {
287         return useJvmChmod;
288     }
289 
290     /**
291      * <b>jvm chmod won't set group level permissions !</b>
292      *
293      * @since 1.1
294      */
295     @Override
296     public void setUseJvmChmod( final boolean useJvmChmod )
297     {
298         this.useJvmChmod = useJvmChmod;
299     }
300 
301     /**
302      * @since 1.1
303      */
304     @Override
305     public boolean isIgnorePermissions()
306     {
307         return ignorePermissions;
308     }
309 
310     /**
311      * @since 1.1
312      */
313     @Override
314     public void setIgnorePermissions( final boolean ignorePermissions )
315     {
316         this.ignorePermissions = ignorePermissions;
317     }
318 
319     protected void extractFile( final File srcF, final File dir, final InputStream compressedInputStream,
320                                 String entryName, final Date entryDate, final boolean isDirectory,
321                                 final Integer mode, String symlinkDestination, final FileMapper[] fileMappers )
322         throws IOException, ArchiverException
323     {
324         if ( fileMappers != null )
325         {
326             for ( final FileMapper fileMapper : fileMappers )
327             {
328                 entryName = fileMapper.getMappedFileName( entryName );
329             }
330         }
331 
332         // Hmm. Symlinks re-evaluate back to the original file here. Unsure if this is a good thing...
333         final File f = FileUtils.resolveFile( dir, entryName );
334 
335         // Make sure that the resolved path of the extracted file doesn't escape the destination directory
336         String canonicalDirPath = dir.getCanonicalPath();
337         String canonicalDestPath = f.getCanonicalPath();
338 
339         if ( !canonicalDestPath.startsWith( canonicalDirPath ) )
340         {
341             throw new ArchiverException( "Entry is outside of the target directory (" + entryName + ")" );
342         }
343 
344         try
345         {
346             if ( !isOverwrite() && f.exists() && ( f.lastModified() >= entryDate.getTime() ) )
347             {
348                 return;
349             }
350 
351             // create intermediary directories - sometimes zip don't add them
352             final File dirF = f.getParentFile();
353             if ( dirF != null )
354             {
355                 dirF.mkdirs();
356             }
357 
358             if ( !StringUtils.isEmpty( symlinkDestination ) )
359             {
360                 SymlinkUtils.createSymbolicLink( f, new File( symlinkDestination ) );
361             }
362             else if ( isDirectory )
363             {
364                 f.mkdirs();
365             }
366             else
367             {
368                 OutputStream out = null;
369                 try
370                 {
371                     out = new FileOutputStream( f );
372 
373                     IOUtil.copy( compressedInputStream, out );
374                     out.close();
375                     out = null;
376                 }
377                 finally
378                 {
379                     IOUtil.close( out );
380                 }
381             }
382 
383             f.setLastModified( entryDate.getTime() );
384 
385             if ( !isIgnorePermissions() && mode != null && !isDirectory )
386             {
387                 ArchiveEntryUtils.chmod( f, mode );
388             }
389         }
390         catch ( final FileNotFoundException ex )
391         {
392             getLogger().warn( "Unable to expand to file " + f.getPath() );
393         }
394     }
395 
396 }