/* * Autopsy Forensic Browser * * Copyright 2015 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.experimental.autoingest; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; import net.sf.sevenzipjbinding.simple.ISimpleInArchive; import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; /** * Set of utilities that handles archive file extraction. Uses 7zip library. */ final class ArchiveUtil { static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS private ArchiveUtil() { } /** * Enum of mime types which support archive extraction */ private enum SupportedArchiveExtractionFormats { ZIP("application/zip"), //NON-NLS SEVENZ("application/x-7z-compressed"), //NON-NLS GZIP("application/gzip"), //NON-NLS XGZIP("application/x-gzip"), //NON-NLS XBZIP2("application/x-bzip2"), //NON-NLS XTAR("application/x-tar"), //NON-NLS XGTAR("application/x-gtar"), XRAR("application/x-rar-compressed"); //NON-NLS private final String mimeType; SupportedArchiveExtractionFormats(final String mimeType) { this.mimeType = mimeType; } @Override public String toString() { return this.mimeType; } } /** * Exception thrown when archive handling resulted in an error */ static class ArchiveExtractionException extends Exception { private static final long serialVersionUID = 1L; ArchiveExtractionException(String message) { super(message); } ArchiveExtractionException(String message, Throwable cause) { super(message, cause); } } /** * This method returns array of supported file extensions. * * @return String array of supported file extensions. */ static String[] getSupportedArchiveTypes(){ return SUPPORTED_EXTENSIONS; } /** * This method returns true if the MIME type is currently supported. Else it * returns false. * * @param mimeType File mime type * * @return This method returns true if the file format is currently * supported. Else it returns false. */ static boolean isExtractionSupportedByMimeType(String mimeType) { for (SupportedArchiveExtractionFormats s : SupportedArchiveExtractionFormats.values()) { if (s.toString().equals(mimeType)) { return true; } } return false; } /** * This method returns true if the file extension is currently supported. * Else it returns false. Attempt extension based detection in case Apache * Tika based detection fails. * * @param extension File extension * * @return This method returns true if the file format is currently * supported. Else it returns false. */ static boolean isExtractionSupportedByFileExtension(String extension) { // attempt extension matching for (String supportedExtension : SUPPORTED_EXTENSIONS) { if (extension.equals(supportedExtension)) { return true; } } return false; } /** * Returns a list of file names contained within an archive. * * @param archiveFilePath Full path to the archive file * * @return List of file names contained within archive * * @throws * ArchiveExtractionException */ static List<String> getListOfFilesWithinArchive(String archiveFilePath) throws ArchiveExtractionException { if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) { try { SevenZip.initSevenZipFromPlatformJAR(); } catch (SevenZipNativeInitializationException ex) { throw new ArchiveExtractionException("AutoIngestDashboard_bnPause_paused", ex); } } List<String> files = new ArrayList<>(); ISevenZipInArchive inArchive = null; try { RandomAccessFile randomAccessFile = new RandomAccessFile(new File(archiveFilePath), "r"); inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile)); final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { files.add(item.getPath()); } } catch (Exception ex) { throw new ArchiveExtractionException("Exception while reading archive contents", ex); } finally { if (inArchive != null) { try { inArchive.close(); } catch (SevenZipException ex) { throw new ArchiveExtractionException("Exception while closing the archive", ex); } } } return files; } /** * Extracts contents of an archive file into a directory. * * @param archiveFilePath Full path to archive. * @param destinationFolder Path to directory where results will be * extracted to. * * @throws * ArchiveExtractionException */ static void unpackArchiveFile(String archiveFilePath, String destinationFolder) throws ArchiveExtractionException { if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) { try { SevenZip.initSevenZipFromPlatformJAR(); } catch (SevenZipNativeInitializationException ex) { throw new ArchiveExtractionException("Unable to initialize 7Zip libraries", ex); } } ISevenZipInArchive inArchive = null; try { RandomAccessFile randomAccessFile = new RandomAccessFile(new File(archiveFilePath), "r"); inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile)); final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); for (ISimpleInArchiveItem entry : simpleInArchive.getArchiveItems()) { String entryPathInArchive = entry.getPath(); Path fullPath = Paths.get(destinationFolder, entryPathInArchive); File destFile = new File(fullPath.toString()); File destinationParent = destFile.getParentFile(); destinationParent.mkdirs(); if (!entry.isFolder()) { UnpackStream unpackStream = null; try { Long size = entry.getSize(); unpackStream = new UnpackStream(destFile.toString(), size); entry.extractSlow(unpackStream); } catch (Exception ex) { throw new ArchiveExtractionException("Exception while unpacking archive contents", ex); } finally { if (unpackStream != null) { unpackStream.close(); } } } } } catch (Exception ex) { throw new ArchiveExtractionException("Exception while unpacking archive contents", ex); } finally { try { if (inArchive != null) { inArchive.close(); } } catch (SevenZipException ex) { throw new ArchiveExtractionException("Exception while closing the archive", ex); } } } /** * Stream used to unpack an archive to local file */ private static class UnpackStream implements ISequentialOutStream { private OutputStream output; private String destFilePath; UnpackStream(String destFilePath, long size) throws ArchiveExtractionException { this.destFilePath = destFilePath; try { output = new FileOutputStream(destFilePath); } catch (IOException ex) { throw new ArchiveExtractionException("Exception while unpacking archive contents", ex); } } @Override public int write(byte[] bytes) throws SevenZipException { try { output.write(bytes); } catch (IOException ex) { throw new SevenZipException("Error writing unpacked file to " + destFilePath, ex); } return bytes.length; } public void close() throws ArchiveExtractionException { if (output != null) { try { output.flush(); output.close(); } catch (IOException ex) { throw new ArchiveExtractionException("Exception while closing the archive", ex); } } } } }