Coverage Report - org.owasp.dependencycheck.utils.ExtractionUtil
 
Classes in this File Line Coverage Branch Coverage Complexity
ExtractionUtil
34%
42/122
31%
12/38
6
 
 1  
 /*
 2  
  * This file is part of dependency-check-core.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  *
 16  
  * Copyright (c) 2013 Jeremy Long. All Rights Reserved.
 17  
  */
 18  
 package org.owasp.dependencycheck.utils;
 19  
 
 20  
 import static org.owasp.dependencycheck.utils.FileUtils.getFileExtension;
 21  
 
 22  
 import java.io.BufferedInputStream;
 23  
 import java.io.BufferedOutputStream;
 24  
 import java.io.Closeable;
 25  
 import java.io.File;
 26  
 import java.io.FileInputStream;
 27  
 import java.io.FileNotFoundException;
 28  
 import java.io.FileOutputStream;
 29  
 import java.io.FilenameFilter;
 30  
 import java.io.IOException;
 31  
 import java.io.InputStream;
 32  
 import java.util.logging.Level;
 33  
 import java.util.logging.Logger;
 34  
 import java.util.zip.ZipEntry;
 35  
 import java.util.zip.ZipInputStream;
 36  
 
 37  
 import org.apache.commons.compress.archivers.ArchiveEntry;
 38  
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 39  
 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
 40  
 import org.owasp.dependencycheck.Engine;
 41  
 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
 42  
 import org.owasp.dependencycheck.analyzer.exception.ArchiveExtractionException;
 43  
 
 44  
 /**
 45  
  * Set of utilities to extract files from archives.
 46  
  *
 47  
  * @author Jeremy Long
 48  
  */
 49  
 public final class ExtractionUtil {
 50  
 
 51  
     /**
 52  
      * The logger.
 53  
      */
 54  1
     private static final Logger LOGGER = Logger.getLogger(ExtractionUtil.class.getName());
 55  
     /**
 56  
      * The buffer size to use when extracting files from the archive.
 57  
      */
 58  
     private static final int BUFFER_SIZE = 4096;
 59  
 
 60  
     /**
 61  
      * Private constructor for a utility class.
 62  
      */
 63  0
     private ExtractionUtil() {
 64  0
     }
 65  
 
 66  
     /**
 67  
      * Extracts the contents of an archive into the specified directory.
 68  
      *
 69  
      * @param archive an archive file such as a WAR or EAR
 70  
      * @param extractTo a directory to extract the contents to
 71  
      * @throws ExtractionException thrown if an exception occurs while extracting the files
 72  
      */
 73  
     public static void extractFiles(File archive, File extractTo) throws ExtractionException {
 74  0
         extractFiles(archive, extractTo, null);
 75  0
     }
 76  
 
 77  
     /**
 78  
      * Extracts the contents of an archive into the specified directory. The files are only extracted if they are supported by the
 79  
      * analyzers loaded into the specified engine. If the engine is specified as null then all files are extracted.
 80  
      *
 81  
      * @param archive an archive file such as a WAR or EAR
 82  
      * @param extractTo a directory to extract the contents to
 83  
      * @param engine the scanning engine
 84  
      * @throws ExtractionException thrown if there is an error extracting the files
 85  
      */
 86  
     public static void extractFiles(File archive, File extractTo, Engine engine) throws ExtractionException {
 87  0
         if (archive == null || extractTo == null) {
 88  0
             return;
 89  
         }
 90  
 
 91  0
         FileInputStream fis = null;
 92  0
         ZipInputStream zis = null;
 93  
 
 94  
         try {
 95  0
             fis = new FileInputStream(archive);
 96  0
         } catch (FileNotFoundException ex) {
 97  0
             LOGGER.log(Level.FINE, null, ex);
 98  0
             throw new ExtractionException("Archive file was not found.", ex);
 99  0
         }
 100  0
         zis = new ZipInputStream(new BufferedInputStream(fis));
 101  
         ZipEntry entry;
 102  
         try {
 103  0
             while ((entry = zis.getNextEntry()) != null) {
 104  0
                 if (entry.isDirectory()) {
 105  0
                     final File d = new File(extractTo, entry.getName());
 106  0
                     if (!d.exists() && !d.mkdirs()) {
 107  0
                         final String msg = String.format("Unable to create '%s'.", d.getAbsolutePath());
 108  0
                         throw new ExtractionException(msg);
 109  
                     }
 110  0
                 } else {
 111  0
                     final File file = new File(extractTo, entry.getName());
 112  0
                     final String ext = getFileExtension(file.getName());
 113  0
                     if (engine == null || engine.supportsExtension(ext)) {
 114  0
                         BufferedOutputStream bos = null;
 115  
                         FileOutputStream fos;
 116  
                         try {
 117  0
                             fos = new FileOutputStream(file);
 118  0
                             bos = new BufferedOutputStream(fos, BUFFER_SIZE);
 119  0
                             transferUsingBuffer(zis, bos);
 120  0
                         } catch (FileNotFoundException ex) {
 121  0
                             LOGGER.log(Level.FINE, null, ex);
 122  0
                             final String msg = String.format("Unable to find file '%s'.", file.getName());
 123  0
                             throw new ExtractionException(msg, ex);
 124  0
                         } catch (IOException ex) {
 125  0
                             LOGGER.log(Level.FINE, null, ex);
 126  0
                             final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
 127  0
                             throw new ExtractionException(msg, ex);
 128  
                         } finally {
 129  0
                             closeStream(bos);
 130  0
                         }
 131  
                     }
 132  0
                 }
 133  
             }
 134  0
         } catch (IOException ex) {
 135  0
             final String msg = String.format("Exception reading archive '%s'.", archive.getName());
 136  0
             LOGGER.log(Level.FINE, msg, ex);
 137  0
             throw new ExtractionException(msg, ex);
 138  
         } finally {
 139  0
             closeStream(zis);
 140  0
         }
 141  0
     }
 142  
 
 143  
     /**
 144  
      * Extracts the contents of an archive into the specified directory.
 145  
      *
 146  
      * @param archive an archive file such as a WAR or EAR
 147  
      * @param destination a directory to extract the contents to
 148  
      * @param filter determines which files get extracted
 149  
      * @throws ExtractionException thrown if the archive is not found
 150  
      */
 151  
     public static void extractFilesUsingFilter(File archive, File destination,
 152  
             FilenameFilter filter) throws ExtractionException {
 153  3
         if (archive == null || destination == null) {
 154  0
             return;
 155  
         }
 156  
 
 157  3
         FileInputStream fis = null;
 158  
         try {
 159  3
             fis = new FileInputStream(archive);
 160  0
         } catch (FileNotFoundException ex) {
 161  0
             LOGGER.log(Level.FINE, null, ex);
 162  0
             throw new ExtractionException("Archive file was not found.", ex);
 163  3
         }
 164  
         try {
 165  3
             extractArchive(new ZipArchiveInputStream(new BufferedInputStream(
 166  
                     fis)), destination, filter);
 167  0
         } catch (ArchiveExtractionException ex) {
 168  0
             final String msg = String.format(
 169  
                     "Exception extracting archive '%s'.", archive.getName());
 170  0
             LOGGER.log(Level.WARNING, msg);
 171  0
             LOGGER.log(Level.FINE, null, ex);
 172  
         } finally {
 173  0
             try {
 174  3
                 fis.close();
 175  0
             } catch (IOException ex) {
 176  0
                 LOGGER.log(Level.FINE, null, ex);
 177  3
             }
 178  0
         }
 179  3
     }
 180  
 
 181  
     /**
 182  
      * Extracts files from an archive.
 183  
      *
 184  
      * @param input the archive to extract files from
 185  
      * @param destination the location to write the files too
 186  
      * @param filter determines which files get extracted
 187  
      * @throws ArchiveExtractionException thrown if there is an exception extracting files from the archive
 188  
      */
 189  
     private static void extractArchive(ArchiveInputStream input,
 190  
             File destination, FilenameFilter filter)
 191  
             throws ArchiveExtractionException {
 192  
         ArchiveEntry entry;
 193  
         try {
 194  36
             while ((entry = input.getNextEntry()) != null) {
 195  33
                 if (entry.isDirectory()) {
 196  0
                     final File dir = new File(destination, entry.getName());
 197  0
                     if (!dir.exists()) {
 198  0
                         if (!dir.mkdirs()) {
 199  0
                             final String msg = String.format(
 200  
                                     "Unable to create directory '%s'.",
 201  
                                     dir.getAbsolutePath());
 202  0
                             throw new AnalysisException(msg);
 203  
                         }
 204  
                     }
 205  0
                 } else {
 206  33
                     extractFile(input, destination, filter, entry);
 207  
                 }
 208  
             }
 209  0
         } catch (IOException ex) {
 210  0
             throw new ArchiveExtractionException(ex);
 211  0
         } catch (Throwable ex) {
 212  0
             throw new ArchiveExtractionException(ex);
 213  
         } finally {
 214  3
             closeStream(input);
 215  3
         }
 216  3
     }
 217  
 
 218  
     /**
 219  
      * Extracts a file from an archive (input stream) and correctly builds the directory structure.
 220  
      *
 221  
      * @param input the archive input stream
 222  
      * @param destination where to write the file
 223  
      * @param filter the file filter to apply to the files being extracted
 224  
      * @param entry the entry from the archive to extract
 225  
      * @throws ExtractionException thrown if there is an error reading from the archive stream
 226  
      */
 227  
     private static void extractFile(ArchiveInputStream input, File destination,
 228  
             FilenameFilter filter, ArchiveEntry entry) throws ExtractionException {
 229  33
         final File file = new File(destination, entry.getName());
 230  33
         if (filter.accept(file.getParentFile(), file.getName())) {
 231  3
             final String extracting = String.format("Extracting '%s'",
 232  
                     file.getPath());
 233  3
             LOGGER.fine(extracting);
 234  3
             BufferedOutputStream bos = null;
 235  3
             FileOutputStream fos = null;
 236  
             try {
 237  3
                 createParentFile(file);
 238  3
                 fos = new FileOutputStream(file);
 239  3
                 bos = new BufferedOutputStream(fos, BUFFER_SIZE);
 240  3
                 transferUsingBuffer(input, bos);
 241  0
             } catch (FileNotFoundException ex) {
 242  0
                 LOGGER.log(Level.FINE, null, ex);
 243  0
                 final String msg = String.format("Unable to find file '%s'.",
 244  
                         file.getName());
 245  0
                 throw new ExtractionException(msg, ex);
 246  0
             } catch (IOException ex) {
 247  0
                 LOGGER.log(Level.FINE, null, ex);
 248  0
                 final String msg = String
 249  
                         .format("IO Exception while parsing file '%s'.",
 250  
                                 file.getName());
 251  0
                 throw new ExtractionException(msg, ex);
 252  
             } finally {
 253  3
                 closeStream(bos);
 254  3
                 closeStream(fos);
 255  3
             }
 256  
         }
 257  33
     }
 258  
 
 259  
     /**
 260  
      * Transfers data from one stream to another using a buffer.
 261  
      *
 262  
      * @param input the input stream
 263  
      * @param bos the output stream
 264  
      * @throws IOException thrown if there is an error reading/writing to the streams
 265  
      */
 266  
     private static void transferUsingBuffer(InputStream input,
 267  
             BufferedOutputStream bos) throws IOException {
 268  
         int count;
 269  3
         final byte[] data = new byte[BUFFER_SIZE];
 270  6
         while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) {
 271  3
             bos.write(data, 0, count);
 272  
         }
 273  3
         bos.flush();
 274  3
     }
 275  
 
 276  
     /**
 277  
      * Closes the stream.
 278  
      *
 279  
      * @param stream the stream to close
 280  
      */
 281  
     private static void closeStream(Closeable stream) {
 282  9
         if (stream != null) {
 283  
             try {
 284  9
                 stream.close();
 285  0
             } catch (IOException ex) {
 286  0
                 LOGGER.log(Level.FINEST, null, ex);
 287  9
             }
 288  
         }
 289  9
     }
 290  
 
 291  
     /**
 292  
      * Ensures the parent path is correctly created on disk so that the file can be extracted to the correct location.
 293  
      *
 294  
      * @param file the file path
 295  
      * @throws ExtractionException thrown if the parent paths could not be created
 296  
      */
 297  
     private static void createParentFile(final File file)
 298  
             throws ExtractionException {
 299  3
         final File parent = file.getParentFile();
 300  3
         if (!parent.isDirectory()) {
 301  3
             if (!parent.mkdirs()) {
 302  0
                 final String msg = String.format(
 303  
                         "Unable to build directory '%s'.",
 304  
                         parent.getAbsolutePath());
 305  0
                 throw new ExtractionException(msg);
 306  
             }
 307  
         }
 308  3
     }
 309  
 
 310  
 }