mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 11:07:43 +00:00
Merge pull request #1007 from rcordovano/file_typing_module_enhancements
File typing module enhancements
This commit is contained in:
commit
b1b37e2e08
@ -30,6 +30,8 @@ import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -211,37 +213,31 @@ public class PlatformUtil {
|
||||
*
|
||||
* @param resourceClass class in the same package as the resourceFile to
|
||||
* extract
|
||||
* @param resourceFile resource file name to extract
|
||||
* @param resourceFileName Name of the resource file to extract
|
||||
* @param overWrite true to overwrite an existing resource
|
||||
* @return true if extracted, false otherwise (if file already exists)
|
||||
* @throws IOException exception thrown if extract the file failed for IO
|
||||
* reasons
|
||||
*/
|
||||
public static <T> boolean extractResourceToUserConfigDir(final Class<T> resourceClass, final String resourceFile, boolean overWrite) throws IOException {
|
||||
final File userDir = new File(getUserConfigDirectory());
|
||||
|
||||
final File resourceFileF = new File(userDir + File.separator + resourceFile);
|
||||
if (resourceFileF.exists() && !overWrite) {
|
||||
public static <T> boolean extractResourceToUserConfigDir(final Class<T> resourceClass, final String resourceFileName, boolean overWrite) throws IOException {
|
||||
Path resourceFilePath = Paths.get(getUserConfigDirectory(), resourceFileName);
|
||||
final File resourceFile = resourceFilePath.toFile();
|
||||
if (resourceFile.exists() && !overWrite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
resourceFileF.getParentFile().mkdirs();
|
||||
InputStream inputStream = resourceClass.getResourceAsStream(resourceFileName);
|
||||
if (null == inputStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InputStream inputStream = resourceClass.getResourceAsStream(resourceFile);
|
||||
|
||||
OutputStream out = null;
|
||||
resourceFile.getParentFile().mkdirs();
|
||||
try (InputStream in = new BufferedInputStream(inputStream)) {
|
||||
|
||||
OutputStream outFile = new FileOutputStream(resourceFileF);
|
||||
out = new BufferedOutputStream(outFile);
|
||||
int readBytes;
|
||||
while ((readBytes = in.read()) != -1) {
|
||||
out.write(readBytes);
|
||||
}
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
out.close();
|
||||
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(resourceFile))) {
|
||||
int readBytes;
|
||||
while ((readBytes = in.read()) != -1) {
|
||||
out.write(readBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -25,6 +25,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
@ -54,6 +55,101 @@ import org.xml.sax.SAXException;
|
||||
*/
|
||||
public class XMLUtil {
|
||||
|
||||
/**
|
||||
* Creates a W3C DOM.
|
||||
*
|
||||
* @return The document object.
|
||||
* @throws ParserConfigurationException
|
||||
*/
|
||||
public static Document createDocument() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
return builder.newDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an XML document into a WC3 DOM and validates it using a schema
|
||||
* packaged as a class resource.
|
||||
*
|
||||
* @param <T> The name of the class associated with the resource.
|
||||
* @param docPath The full path to the XML document.
|
||||
* @param clazz The class associated with the schema resource.
|
||||
* @param schemaResourceName The name of the schema resource.
|
||||
* @return The WC3 DOM document object.
|
||||
* @throws IOException
|
||||
* @throws ParserConfigurationException
|
||||
* @throws SAXException
|
||||
*/
|
||||
public static <T> Document loadDocument(String docPath, Class<T> clazz, String schemaResourceName) throws IOException, ParserConfigurationException, SAXException {
|
||||
Document doc = loadDocument(docPath);
|
||||
validateDocument(doc, clazz, schemaResourceName);
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an XML document into a WC3 DOM.
|
||||
*
|
||||
* @param docPath The full path to the XML document.
|
||||
* @return The WC3 DOM document object.
|
||||
* @throws ParserConfigurationException
|
||||
* @throws SAXException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Document loadDocument(String docPath) throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
Document doc = builder.parse(new FileInputStream(docPath));
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a WC3 DOM using a schema packaged as a class resource.
|
||||
*
|
||||
* @param doc
|
||||
* @param clazz
|
||||
* @param schemaResourceName
|
||||
* @throws SAXException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static <T> void validateDocument(final Document doc, Class<T> clazz, String schemaResourceName) throws SAXException, IOException {
|
||||
PlatformUtil.extractResourceToUserConfigDir(clazz, schemaResourceName, false);
|
||||
File schemaFile = new File(Paths.get(PlatformUtil.getUserConfigDirectory(), schemaResourceName).toAbsolutePath().toString());
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
Schema schema = schemaFactory.newSchema(schemaFile);
|
||||
Validator validator = schema.newValidator();
|
||||
validator.validate(new DOMSource(doc), new DOMResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a WC3 DOM by writing it to an XML document.
|
||||
*
|
||||
* @param doc The WC3 DOM document object.
|
||||
* @param docPath The full path to the XML document.
|
||||
* @param encoding Encoding scheme to use for the XML document, e.g.,
|
||||
* "UTF-8."
|
||||
* @throws TransformerConfigurationException
|
||||
* @throws FileNotFoundException
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws TransformerException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void saveDocument(final Document doc, String encoding, String docPath) throws TransformerConfigurationException, FileNotFoundException, UnsupportedEncodingException, TransformerException, IOException {
|
||||
TransformerFactory xf = TransformerFactory.newInstance();
|
||||
xf.setAttribute("indent-number", 1); //NON-NLS
|
||||
Transformer xformer = xf.newTransformer();
|
||||
xformer.setOutputProperty(OutputKeys.METHOD, "xml"); //NON-NLS
|
||||
xformer.setOutputProperty(OutputKeys.INDENT, "yes"); //NON-NLS
|
||||
xformer.setOutputProperty(OutputKeys.ENCODING, encoding);
|
||||
xformer.setOutputProperty(OutputKeys.STANDALONE, "yes"); //NON-NLS
|
||||
xformer.setOutputProperty(OutputKeys.VERSION, "1.0");
|
||||
File file = new File(docPath);
|
||||
try (FileOutputStream stream = new FileOutputStream(file)) {
|
||||
Result out = new StreamResult(new OutputStreamWriter(stream, encoding));
|
||||
xformer.transform(new DOMSource(doc), out);
|
||||
stream.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to validate XML files against pre-defined schema files.
|
||||
*
|
||||
@ -71,6 +167,7 @@ public class XMLUtil {
|
||||
* IngestModuleLoader.
|
||||
*
|
||||
*/
|
||||
// TODO: Deprecate.
|
||||
public static <T> boolean xmlIsValid(DOMSource xmlfile, Class<T> clazz, String schemaFile) {
|
||||
try {
|
||||
PlatformUtil.extractResourceToUserConfigDir(clazz, schemaFile, false);
|
||||
@ -108,6 +205,7 @@ public class XMLUtil {
|
||||
* IngestModuleLoader.
|
||||
*
|
||||
*/
|
||||
// TODO: Deprecate.
|
||||
public static <T> boolean xmlIsValid(Document doc, Class<T> clazz, String type) {
|
||||
DOMSource dms = new DOMSource(doc);
|
||||
return xmlIsValid(dms, clazz, type);
|
||||
@ -119,6 +217,7 @@ public class XMLUtil {
|
||||
* @param clazz the class this method is invoked from
|
||||
* @param xmlPath the full path to the file to load
|
||||
*/
|
||||
// TODO: Deprecate.
|
||||
public static <T> Document loadDoc(Class<T> clazz, String xmlPath) {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
Document ret = null;
|
||||
@ -143,6 +242,7 @@ public class XMLUtil {
|
||||
* @param xmlPath the full path to the file to load
|
||||
* @param xsdPath the full path to the file to validate against
|
||||
*/
|
||||
// TODO: Deprecate
|
||||
public static <T> Document loadDoc(Class<T> clazz, String xmlPath, String xsdPath) {
|
||||
Document ret = loadDoc(clazz, xmlPath);
|
||||
if (!XMLUtil.xmlIsValid(ret, clazz, xsdPath)) {
|
||||
@ -159,9 +259,10 @@ public class XMLUtil {
|
||||
* @param encoding to encoding, such as "UTF-8", to encode the file with
|
||||
* @param doc the document to save
|
||||
*/
|
||||
// TODO: Deprecate.
|
||||
public static <T> boolean saveDoc(Class<T> clazz, String xmlPath, String encoding, final Document doc) {
|
||||
TransformerFactory xf = TransformerFactory.newInstance();
|
||||
xf.setAttribute("indent-number", new Integer(1)); //NON-NLS
|
||||
xf.setAttribute("indent-number", 1); //NON-NLS
|
||||
boolean success = false;
|
||||
try {
|
||||
Transformer xformer = xf.newTransformer();
|
||||
@ -191,4 +292,5 @@ public class XMLUtil {
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
OpenIDE-Module-Name=FileTypeId
|
||||
FileTypeIdModuleSettingsPanel.skipKnownCheckBox.toolTipText=Depending on how many files have known hashes, checking this box will improve the speed of file type identification.
|
||||
FileTypeIdIngestModule.moduleName.text=File Type Identification
|
||||
FileTypeIdIngestModule.moduleDesc.text=Matches file types based on binary signatures.
|
||||
FileTypeIdIngestModule.complete.totalProcTime=Total Processing Time
|
||||
@ -7,4 +6,38 @@ FileTypeIdIngestModule.complete.totalFiles=Total Files Processed
|
||||
FileTypeIdIngestModule.complete.srvMsg.text=File Type Id Results
|
||||
FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg=Expected settings argument to be instanceof FileTypeIdModuleSettings
|
||||
FileTypeIdModuleFactory.createFileIngestModule.exception.msg=Expected settings argument to be instanceof FileTypeIdModuleSettings
|
||||
FileTypeIdModuleSettingsPanel.skipKnownCheckBox.text=Skip known files (NSRL)
|
||||
FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText=Depending on how many files have known hashes, checking this box will improve the speed of file type identification.
|
||||
FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text=Skip known files (NSRL)
|
||||
FileTypeIdGlobalSettingsPanel.hexPrefixLabel.text=0x
|
||||
FileTypeIdGlobalSettingsPanel.deleteTypeButton.text=DeleteType
|
||||
FileTypeIdGlobalSettingsPanel.offsetTextField.text=
|
||||
FileTypeIdGlobalSettingsPanel.offsetLabel.text=Offset
|
||||
FileTypeIdGlobalSettingsPanel.postHitCheckBox.text=Post interesting file hit when found
|
||||
FileTypeIdGlobalSettingsPanel.signatureTextField.text=
|
||||
FileTypeIdGlobalSettingsPanel.signatureTypeLabel.text=Signature Type
|
||||
FileTypeIdGlobalSettingsPanel.mimeTypeTextField.text=
|
||||
FileTypeIdGlobalSettingsPanel.signatureLabel.text=Signature
|
||||
FileTypeIdGlobalSettingsPanel.mimeTypeLabel.text=MIME Type
|
||||
FileTypeIdGlobalSettingsPanel.saveTypeButton.text=Save Type
|
||||
FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem=Bytes (Hex)
|
||||
FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem=String (ASCII)
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.message=MIME type is required.
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.title=Missing MIME Type
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.message=Signature is required.
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.title=Missing Signature
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.message=Offset must be a positive integer.
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title=Invalid Offset
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidRawSignatureBytes.message=The signature has one or more invalid hexadecimal digits.
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignatureBytes.title=Invalid Signature
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message=Interesting files set name is required if alert is requests.
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title=Missing Interesting Files Set Name
|
||||
FileTypeIdGlobalSettingsPanel.filesSetNameLabel.text=Files Set Name
|
||||
FileTypeIdGlobalSettingsPanel.filesSetNameTextField.text=
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title=Save Failed
|
||||
FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title=Load Failed
|
||||
FileTypeIdGlobalSettingsPanel.hintTextArea.text=Enter a MIME type and signature to be used to identify files of that type. If the signature is a byte sequence, enter the sequence using two hex values for each byte, e.g., EEF0 is a two byte signature.
|
||||
FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text=Cannot make changes to file type definitions when ingest is running!
|
||||
UserDefinedFileTypesManager.loadFileTypes.errorMessage=Failed to load existing file type definitions.
|
||||
UserDefinedFileTypesManager.saveFileTypes.errorMessage=Failed to save file type definitions.
|
||||
FileTypeIdIngestJobSettingsPanel.skipSmallFilesCheckBox.text=Skip files smaller than {0} bytes
|
||||
FileTypeIdGlobalSettingsPanel.newTypeButton.text=New Type
|
||||
|
@ -1,10 +1,10 @@
|
||||
OpenIDE-Module-Name=\u30D5\u30A1\u30A4\u30EB\u30BF\u30A4\u30D7\u306E\u7279\u5B9A
|
||||
FileTypeIdModuleSettingsPanel.skipKnownCheckBox.toolTipText=\u65E2\u77E5\u306E\u30CF\u30C3\u30B7\u30E5\u5024\u3092\u6301\u3064\u30D5\u30A1\u30A4\u30EB\u6570\u306B\u3088\u3063\u3066\u306F\u3001\u3053\u306E\u30DC\u30C3\u30AF\u30B9\u3092\u9078\u629E\u3059\u308B\u306E\u306B\u3088\u308A\u3001\u30D5\u30A1\u30A4\u30EB\u30BF\u30A4\u30D7\u306E\u7279\u5B9A\u3092\u52A0\u901F\u3057\u307E\u3059\u3002
|
||||
FileTypeIdIngestModule.moduleName.text=\u30D5\u30A1\u30A4\u30EB\u30BF\u30A4\u30D7\u306E\u7279\u5B9A
|
||||
FileTypeIdIngestModule.moduleDesc.text=\u30D0\u30A4\u30CA\u30EA\u7F72\u540D\u306B\u57FA\u3065\u3044\u3066\u30D5\u30A1\u30A4\u30EB\u30BF\u30A4\u30D7\u3092\u4E00\u81F4\u3059\u308B\u3002
|
||||
FileTypeIdIngestModule.complete.totalProcTime=\u5408\u8A08\u51E6\u7406\u6642\u9593
|
||||
FileTypeIdIngestModule.complete.totalFiles=\u5408\u8A08\u51E6\u7406\u30D5\u30A1\u30A4\u30EB\u6570
|
||||
FileTypeIdIngestModule.complete.srvMsg.text=\u30D5\u30A1\u30A4\u30EB\u30BF\u30A4\u30D7\u7279\u5B9A\u306E\u7D50\u679C
|
||||
FileTypeIdModuleSettingsPanel.skipKnownCheckBox.text=\u65E2\u77E5\u30D5\u30A1\u30A4\u30EB\uFF08NSRL\uFF09\u3092\u30B9\u30AD\u30C3\u30D7
|
||||
FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg=\u8A2D\u5B9A\u3092\u884C\u3046\u70BA\u306E\u60F3\u5B9A\u3055\u308C\u308B\u5F15\u6570\u306Finstanceof FileTypeIdModuleSettings\u3067\u3059\u3002
|
||||
FileTypeIdModuleFactory.createFileIngestModule.exception.msg=\u8A2D\u5B9A\u3092\u884C\u3046\u70BA\u306E\u60F3\u5B9A\u3055\u308C\u308B\u5F15\u6570\u306Finstanceof FileTypeIdModuleSettings\u3067\u3059\u3002
|
||||
OpenIDE-Module-Name=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u7279\u5b9a
|
||||
FileTypeIdIngestModule.moduleName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u7279\u5b9a
|
||||
FileTypeIdIngestModule.moduleDesc.text=\u30d0\u30a4\u30ca\u30ea\u7f72\u540d\u306b\u57fa\u3065\u3044\u3066\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u4e00\u81f4\u3059\u308b\u3002
|
||||
FileTypeIdIngestModule.complete.totalProcTime=\u5408\u8a08\u51e6\u7406\u6642\u9593
|
||||
FileTypeIdIngestModule.complete.totalFiles=\u5408\u8a08\u51e6\u7406\u30d5\u30a1\u30a4\u30eb\u6570
|
||||
FileTypeIdIngestModule.complete.srvMsg.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u7279\u5b9a\u306e\u7d50\u679c
|
||||
FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof FileTypeIdModuleSettings\u3067\u3059\u3002
|
||||
FileTypeIdModuleFactory.createFileIngestModule.exception.msg=\u8a2d\u5b9a\u3092\u884c\u3046\u70ba\u306e\u60f3\u5b9a\u3055\u308c\u308b\u5f15\u6570\u306finstanceof FileTypeIdModuleSettings\u3067\u3059\u3002
|
||||
FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u6570\u306b\u3088\u3063\u3066\u306f\u3001\u3053\u306e\u30dc\u30c3\u30af\u30b9\u3092\u9078\u629e\u3059\u308b\u306e\u306b\u3088\u308a\u3001\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u306e\u7279\u5b9a\u3092\u52a0\u901f\u3057\u307e\u3059\u3002
|
||||
FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\uff08NSRL\uff09\u3092\u30b9\u30ad\u30c3\u30d7
|
||||
|
193
Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java
Normal file
193
Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.modules.filetypeid;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Represents a file type characterized by a file signature.
|
||||
* <p>
|
||||
* Thread-safe (immutable).
|
||||
*/
|
||||
class FileType {
|
||||
|
||||
private final String mimeType;
|
||||
private final Signature signature;
|
||||
private final String interestingFilesSetName;
|
||||
private final boolean alert;
|
||||
|
||||
/**
|
||||
* Creates a representation of a file type characterized by a file
|
||||
* signature.
|
||||
*
|
||||
* @param mimeType The mime type to associate with this file type.
|
||||
* @param signature The signature that characterizes this file type.
|
||||
* @param filesSetName The name of an interesting files set that includes
|
||||
* files of this type, may be the empty string.
|
||||
* @param alert Whether the user wishes to be alerted when a file matching
|
||||
* this type is encountered.
|
||||
*/
|
||||
FileType(String mimeType, final Signature signature, String filesSetName, boolean alert) {
|
||||
this.mimeType = mimeType;
|
||||
this.signature = new Signature(signature.getSignatureBytes(), signature.getOffset(), signature.getType());
|
||||
this.interestingFilesSetName = filesSetName;
|
||||
this.alert = alert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MIME type associated with this file type.
|
||||
*
|
||||
* @return The MIME type.
|
||||
*/
|
||||
String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the signature associated with this file type.
|
||||
*
|
||||
* @return The signature.
|
||||
*/
|
||||
Signature getSignature() {
|
||||
return new Signature(signature.getSignatureBytes(), signature.getOffset(), signature.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not a file is an instance of this file type.
|
||||
*
|
||||
* @param file The file to test.
|
||||
* @return True or false.
|
||||
*/
|
||||
boolean matches(final AbstractFile file) {
|
||||
return signature.containedIn(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not an alert is desired if a file of this type is
|
||||
* encountered.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
boolean alertOnMatch() {
|
||||
return alert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of an interesting files set that includes files of this
|
||||
* type.
|
||||
*
|
||||
* @return The interesting files set name, possibly empty.
|
||||
*/
|
||||
String getFilesSetName() {
|
||||
return interestingFilesSetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* A file signature consisting of a sequence of bytes at a specific offset
|
||||
* within a file.
|
||||
* <p>
|
||||
* Thread-safe (immutable).
|
||||
*/
|
||||
static class Signature {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Signature.class.getName());
|
||||
|
||||
/**
|
||||
* The way the signature byte sequence should be interpreted.
|
||||
*/
|
||||
enum Type {
|
||||
|
||||
RAW, ASCII
|
||||
};
|
||||
|
||||
private final byte[] signatureBytes;
|
||||
private final long offset;
|
||||
private final Type type;
|
||||
|
||||
/**
|
||||
* Creates a file signature consisting of a sequence of bytes at a
|
||||
* specific offset within a file.
|
||||
*
|
||||
* @param signatureBytes The signature bytes.
|
||||
* @param offset The offset of the signature bytes.
|
||||
* @param type The interpretation of the signature bytes (e.g., raw
|
||||
* bytes, an ASCII string).
|
||||
*/
|
||||
Signature(final byte[] signatureBytes, long offset, Type type) {
|
||||
this.signatureBytes = Arrays.copyOf(signatureBytes, signatureBytes.length);
|
||||
this.offset = offset;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte sequence of the signature.
|
||||
*
|
||||
* @return The byte sequence as an array of bytes.
|
||||
*/
|
||||
byte[] getSignatureBytes() {
|
||||
return Arrays.copyOf(signatureBytes, signatureBytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the offset of the signature.
|
||||
*
|
||||
* @return The offset.
|
||||
*/
|
||||
long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interpretation of the byte sequence for the signature.
|
||||
*
|
||||
* @return The signature type.
|
||||
*/
|
||||
Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the signature is contained within a given
|
||||
* file.
|
||||
*
|
||||
* @param file The file to test
|
||||
* @return True or false.
|
||||
*/
|
||||
boolean containedIn(final AbstractFile file) {
|
||||
try {
|
||||
byte[] buffer = new byte[signatureBytes.length];
|
||||
int bytesRead = file.read(buffer, offset, signatureBytes.length);
|
||||
return ((bytesRead == signatureBytes.length) && (Arrays.equals(buffer, signatureBytes)));
|
||||
} catch (TskCoreException ex) {
|
||||
/**
|
||||
* This exception is caught rather than propagated because files
|
||||
* in images are not always consistent with their file system
|
||||
* meta data making for read errors.
|
||||
*/
|
||||
Signature.logger.log(Level.WARNING, "Error reading from file with objId = " + file.getId(), ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,346 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[500, 300]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[500, 300]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="ingestRunningWarningLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="30" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="typesScrollPane" pref="0" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="deleteTypeButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="97" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="separator" min="-2" pref="13" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="offsetLabel" min="-2" pref="71" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="offsetTextField" min="-2" pref="178" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="1" max="-2" attributes="0">
|
||||
<Component id="signatureLabel" max="32767" attributes="0"/>
|
||||
<Component id="signatureTypeLabel" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
|
||||
<Component id="mimeTypeLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="mimeTypeTextField" min="-2" pref="176" max="-2" attributes="0"/>
|
||||
<Component id="signatureTypeComboBox" min="-2" pref="176" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="hexPrefixLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="signatureTextField" min="-2" pref="160" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<Component id="hintScrollPane" alignment="0" min="-2" pref="260" max="-2" attributes="0"/>
|
||||
<Component id="postHitCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
|
||||
<Component id="filesSetNameTextField" alignment="1" min="-2" pref="179" max="-2" attributes="0"/>
|
||||
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="newTypeButton" linkSize="1" min="-2" pref="71" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="saveTypeButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="filesSetNameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="188" pref="188" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="hintScrollPane" min="-2" pref="76" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="mimeTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="mimeTypeTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="signatureTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="signatureTypeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="signatureTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="hexPrefixLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="signatureLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="offsetTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="offsetLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="postHitCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="filesSetNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="filesSetNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Component id="typesScrollPane" min="-2" pref="219" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="newTypeButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="deleteTypeButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="saveTypeButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="11" pref="11" max="-2" attributes="0"/>
|
||||
<Component id="separator" min="-2" pref="257" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="ingestRunningWarningLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="typesScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JList" name="typesList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[200, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JSeparator" name="separator">
|
||||
<Properties>
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="mimeTypeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.mimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="mimeTypeTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.mimeTypeTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="signatureTypeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.signatureTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="signatureTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.signatureTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="offsetLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.offsetLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="offsetTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.offsetTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="newTypeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.newTypeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newTypeButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="deleteTypeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.deleteTypeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteTypeButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="saveTypeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.saveTypeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveTypeButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="hexPrefixLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.hexPrefixLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="signatureTypeComboBox">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="signatureTypeComboBoxActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="signatureLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.signatureLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="hintScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="hintTextArea">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="11" style="0"/>
|
||||
</Property>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="5"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.hintTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[164, 70]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JCheckBox" name="postHitCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.postHitCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="postHitCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="filesSetNameLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.filesSetNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="filesSetNameTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.filesSetNameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="ingestRunningWarningLabel">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/modules/filetypeid/warning16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,672 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.modules.filetypeid;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException;
|
||||
|
||||
/**
|
||||
* A panel to allow a user to make custom file type definitions. In addition to
|
||||
* being an ingest module global settings panel, an instance of this class also
|
||||
* appears in the NetBeans options dialog as an options panel.
|
||||
*/
|
||||
final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
|
||||
|
||||
private static final String RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem");
|
||||
private static final String ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem");
|
||||
|
||||
/**
|
||||
* The list model for the file types list component of this panel is the set
|
||||
* of MIME types associated with the user-defined file types. A mapping of
|
||||
* the MIME types to file type objects lies behind the list model. This map
|
||||
* is obtained from the user-defined types manager.
|
||||
*/
|
||||
private DefaultListModel<String> typesListModel;
|
||||
private Map<String, FileType> fileTypes;
|
||||
|
||||
/**
|
||||
* This panel implements a property change listener that listens to ingest
|
||||
* job events so it can disable the buttons on the panel if ingest is
|
||||
* running. This is done to prevent changes to user-defined types while the
|
||||
* type definitions are in use.
|
||||
*/
|
||||
// TODO: Disabling during ingest would not be necessary if the file ingest
|
||||
// modules obtained and shared a per data source ingest job snapshot of the
|
||||
// file type definitions.
|
||||
IngestJobEventPropertyChangeListener ingestJobEventsListener;
|
||||
|
||||
/**
|
||||
* Creates a panel to allow a user to make custom file type definitions.
|
||||
*/
|
||||
FileTypeIdGlobalSettingsPanel() {
|
||||
initComponents();
|
||||
customizeComponents();
|
||||
addIngestJobEventsListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does child component initialization in addition to that done by the
|
||||
* Matisse generated code.
|
||||
*/
|
||||
private void customizeComponents() {
|
||||
setFileTypesListModel();
|
||||
setSignatureTypeComboBoxModel();
|
||||
clearTypeDetailsComponents();
|
||||
addTypeListSelectionListener();
|
||||
addTextFieldListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list model for the list of file types.
|
||||
*/
|
||||
private void setFileTypesListModel() {
|
||||
typesListModel = new DefaultListModel<>();
|
||||
typesList.setModel(typesListModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model for the signature type combo box.
|
||||
*/
|
||||
private void setSignatureTypeComboBoxModel() {
|
||||
DefaultComboBoxModel<String> sigTypeComboBoxModel = new DefaultComboBoxModel<>();
|
||||
sigTypeComboBoxModel.addElement(FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM);
|
||||
sigTypeComboBoxModel.addElement(FileTypeIdGlobalSettingsPanel.ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM);
|
||||
signatureTypeComboBox.setModel(sigTypeComboBoxModel);
|
||||
signatureTypeComboBox.setSelectedItem(FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to the types list component so that the components in the
|
||||
* file type details section of the panel can be populated and cleared based
|
||||
* on the selection in the list.
|
||||
*/
|
||||
private void addTypeListSelectionListener() {
|
||||
typesList.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting() == false) {
|
||||
if (typesList.getSelectedIndex() == -1) {
|
||||
clearTypeDetailsComponents();
|
||||
} else {
|
||||
populateTypeDetailsComponents();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds listeners to the text fields that enable and disable the buttons on
|
||||
* the panel.
|
||||
*/
|
||||
private void addTextFieldListeners() {
|
||||
DocumentListener listener = new DocumentListener() {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
enableButtons();
|
||||
}
|
||||
};
|
||||
|
||||
mimeTypeTextField.getDocument().addDocumentListener(listener);
|
||||
offsetTextField.getDocument().addDocumentListener(listener);
|
||||
signatureTextField.getDocument().addDocumentListener(listener);
|
||||
filesSetNameTextField.getDocument().addDocumentListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property change listener that listens to ingest job events to
|
||||
* disable the buttons on the panel if ingest is running. This is done to
|
||||
* prevent changes to user-defined types while the type definitions are in
|
||||
* use.
|
||||
*/
|
||||
// TODO: Disabling during ingest would not be necessary if the file ingest
|
||||
// modules obtained and shared a per data source ingest job snapshot of the
|
||||
// file type definitions.
|
||||
private void addIngestJobEventsListener() {
|
||||
ingestJobEventsListener = new IngestJobEventPropertyChangeListener();
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventsListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* A property change listener that listens to ingest job events.
|
||||
*/
|
||||
private class IngestJobEventPropertyChangeListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
enableButtons();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the panel buttons based on the state of the panel and
|
||||
* the application.
|
||||
*/
|
||||
private void enableButtons() {
|
||||
boolean ingestIsRunning = IngestManager.getInstance().isIngestRunning();
|
||||
newTypeButton.setEnabled(!ingestIsRunning);
|
||||
|
||||
boolean fileTypeIsSelected = typesList.getSelectedIndex() != -1;
|
||||
deleteTypeButton.setEnabled(!ingestIsRunning && fileTypeIsSelected);
|
||||
|
||||
boolean requiredFieldsPopulated
|
||||
= !mimeTypeTextField.getText().isEmpty()
|
||||
&& !offsetTextField.getText().isEmpty()
|
||||
&& !signatureTextField.getText().isEmpty()
|
||||
&& (postHitCheckBox.isSelected() ? !filesSetNameTextField.getText().isEmpty() : true);
|
||||
saveTypeButton.setEnabled(!ingestIsRunning && requiredFieldsPopulated);
|
||||
|
||||
ingestRunningWarningLabel.setVisible(ingestIsRunning);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void load() {
|
||||
try {
|
||||
fileTypes = UserDefinedFileTypesManager.getInstance().getUserDefinedFileTypes();
|
||||
updateFileTypesListModel();
|
||||
if (!typesListModel.isEmpty()) {
|
||||
typesList.setSelectedIndex(0);
|
||||
}
|
||||
} catch (UserDefinedFileTypesException ex) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
ex.getLocalizedMessage(),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
fileTypes = Collections.emptyMap();
|
||||
}
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list model for the file types list component.
|
||||
*/
|
||||
private void updateFileTypesListModel() {
|
||||
ArrayList<String> mimeTypes = new ArrayList<>(fileTypes.keySet());
|
||||
Collections.sort(mimeTypes);
|
||||
typesListModel.clear();
|
||||
for (String mimeType : mimeTypes) {
|
||||
typesListModel.addElement(mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates all of the components in the file type details portion of the
|
||||
* panel based on the current selection in the file types list.
|
||||
*/
|
||||
private void populateTypeDetailsComponents() {
|
||||
String mimeType = typesList.getSelectedValue();
|
||||
FileType fileType = fileTypes.get(mimeType);
|
||||
if (null != fileType) {
|
||||
mimeTypeTextField.setText(fileType.getMimeType());
|
||||
Signature signature = fileType.getSignature();
|
||||
FileType.Signature.Type sigType = signature.getType();
|
||||
signatureTypeComboBox.setSelectedItem(sigType == FileType.Signature.Type.RAW ? FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM : FileTypeIdGlobalSettingsPanel.ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM);
|
||||
String signatureBytes;
|
||||
if (Signature.Type.RAW == signature.getType()) {
|
||||
signatureBytes = DatatypeConverter.printHexBinary(signature.getSignatureBytes());
|
||||
} else {
|
||||
try {
|
||||
signatureBytes = new String(signature.getSignatureBytes(), "UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
ex.getLocalizedMessage(),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
signatureBytes = "";
|
||||
}
|
||||
}
|
||||
signatureTextField.setText(signatureBytes);
|
||||
offsetTextField.setText(Long.toString(signature.getOffset()));
|
||||
postHitCheckBox.setSelected(fileType.alertOnMatch());
|
||||
filesSetNameTextField.setEnabled(postHitCheckBox.isSelected());
|
||||
filesSetNameTextField.setText(fileType.getFilesSetName());
|
||||
}
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all of the components in the individual type details portion of
|
||||
* the panel.
|
||||
*/
|
||||
private void clearTypeDetailsComponents() {
|
||||
typesList.clearSelection();
|
||||
mimeTypeTextField.setText(""); //NON-NLS
|
||||
signatureTypeComboBox.setSelectedItem(FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM);
|
||||
hexPrefixLabel.setVisible(true);
|
||||
signatureTextField.setText(""); //NON-NLS
|
||||
offsetTextField.setText(""); //NON-NLS
|
||||
postHitCheckBox.setSelected(false);
|
||||
filesSetNameTextField.setText(""); //NON-NLS
|
||||
filesSetNameTextField.setEnabled(false);
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void store() {
|
||||
try {
|
||||
UserDefinedFileTypesManager.getInstance().setUserDefinedFileTypes(fileTypes);
|
||||
} catch (UserDefinedFileTypesManager.UserDefinedFileTypesException ex) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
ex.getLocalizedMessage(),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void saveSettings() {
|
||||
store();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("FinalizeDeclaration")
|
||||
protected void finalize() throws Throwable {
|
||||
IngestManager.getInstance().removeIngestJobEventListener(ingestJobEventsListener);
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
typesScrollPane = new javax.swing.JScrollPane();
|
||||
typesList = new javax.swing.JList<String>();
|
||||
separator = new javax.swing.JSeparator();
|
||||
mimeTypeLabel = new javax.swing.JLabel();
|
||||
mimeTypeTextField = new javax.swing.JTextField();
|
||||
signatureTypeLabel = new javax.swing.JLabel();
|
||||
signatureTextField = new javax.swing.JTextField();
|
||||
offsetLabel = new javax.swing.JLabel();
|
||||
offsetTextField = new javax.swing.JTextField();
|
||||
newTypeButton = new javax.swing.JButton();
|
||||
deleteTypeButton = new javax.swing.JButton();
|
||||
saveTypeButton = new javax.swing.JButton();
|
||||
hexPrefixLabel = new javax.swing.JLabel();
|
||||
signatureTypeComboBox = new javax.swing.JComboBox<String>();
|
||||
signatureLabel = new javax.swing.JLabel();
|
||||
hintScrollPane = new javax.swing.JScrollPane();
|
||||
hintTextArea = new javax.swing.JTextArea();
|
||||
postHitCheckBox = new javax.swing.JCheckBox();
|
||||
filesSetNameLabel = new javax.swing.JLabel();
|
||||
filesSetNameTextField = new javax.swing.JTextField();
|
||||
ingestRunningWarningLabel = new javax.swing.JLabel();
|
||||
|
||||
setMaximumSize(new java.awt.Dimension(500, 300));
|
||||
setPreferredSize(new java.awt.Dimension(500, 300));
|
||||
|
||||
typesList.setMinimumSize(new java.awt.Dimension(200, 0));
|
||||
typesScrollPane.setViewportView(typesList);
|
||||
|
||||
separator.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(mimeTypeLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.mimeTypeLabel.text")); // NOI18N
|
||||
|
||||
mimeTypeTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.mimeTypeTextField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(signatureTypeLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureTypeLabel.text")); // NOI18N
|
||||
|
||||
signatureTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureTextField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(offsetLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetLabel.text")); // NOI18N
|
||||
|
||||
offsetTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetTextField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(newTypeButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.newTypeButton.text")); // NOI18N
|
||||
newTypeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
newTypeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deleteTypeButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.deleteTypeButton.text")); // NOI18N
|
||||
deleteTypeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
deleteTypeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(saveTypeButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.saveTypeButton.text")); // NOI18N
|
||||
saveTypeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
saveTypeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(hexPrefixLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.hexPrefixLabel.text")); // NOI18N
|
||||
|
||||
signatureTypeComboBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
signatureTypeComboBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(signatureLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureLabel.text")); // NOI18N
|
||||
|
||||
hintTextArea.setEditable(false);
|
||||
hintTextArea.setColumns(20);
|
||||
hintTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
|
||||
hintTextArea.setLineWrap(true);
|
||||
hintTextArea.setRows(5);
|
||||
hintTextArea.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.hintTextArea.text")); // NOI18N
|
||||
hintTextArea.setWrapStyleWord(true);
|
||||
hintTextArea.setPreferredSize(new java.awt.Dimension(164, 70));
|
||||
hintScrollPane.setViewportView(hintTextArea);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(postHitCheckBox, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.postHitCheckBox.text")); // NOI18N
|
||||
postHitCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
postHitCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(filesSetNameLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.filesSetNameLabel.text")); // NOI18N
|
||||
|
||||
filesSetNameTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.filesSetNameTextField.text")); // NOI18N
|
||||
|
||||
ingestRunningWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/filetypeid/warning16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ingestRunningWarningLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.ingestRunningWarningLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(ingestRunningWarningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(30, 30, 30))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(typesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(deleteTypeButton)
|
||||
.addGap(0, 97, Short.MAX_VALUE)))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(offsetLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(offsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
|
||||
.addComponent(signatureLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(signatureTypeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
|
||||
.addGap(2, 2, 2)
|
||||
.addComponent(mimeTypeLabel)))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(mimeTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(hexPrefixLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(signatureTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)))))
|
||||
.addComponent(hintScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 260, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(postHitCheckBox)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(filesSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 179, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(newTypeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(saveTypeButton))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(filesSetNameLabel)
|
||||
.addGap(188, 188, 188)))))
|
||||
.addGap(29, 29, 29))))
|
||||
);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteTypeButton, newTypeButton, saveTypeButton});
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(hintScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(mimeTypeLabel)
|
||||
.addComponent(mimeTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(signatureTypeLabel)
|
||||
.addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(signatureTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(hexPrefixLabel)
|
||||
.addComponent(signatureLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(offsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(offsetLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(postHitCheckBox)
|
||||
.addGap(1, 1, 1)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(filesSetNameLabel)
|
||||
.addComponent(filesSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addComponent(typesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 219, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(newTypeButton)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(deleteTypeButton)
|
||||
.addComponent(saveTypeButton))))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(11, 11, 11)
|
||||
.addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(ingestRunningWarningLabel)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteTypeButton, newTypeButton, saveTypeButton});
|
||||
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void newTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTypeButtonActionPerformed
|
||||
clearTypeDetailsComponents();
|
||||
}//GEN-LAST:event_newTypeButtonActionPerformed
|
||||
|
||||
private void deleteTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTypeButtonActionPerformed
|
||||
String typeName = typesList.getSelectedValue();
|
||||
fileTypes.remove(typeName);
|
||||
updateFileTypesListModel();
|
||||
if (!typesListModel.isEmpty()) {
|
||||
typesList.setSelectedIndex(0);
|
||||
}
|
||||
}//GEN-LAST:event_deleteTypeButtonActionPerformed
|
||||
|
||||
private void saveTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveTypeButtonActionPerformed
|
||||
/**
|
||||
* Get the MIME type.
|
||||
*/
|
||||
String typeName = mimeTypeTextField.getText();
|
||||
if (typeName.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.message"),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidMIMEType.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signature type.
|
||||
*/
|
||||
FileType.Signature.Type sigType = signatureTypeComboBox.getSelectedItem() == FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM ? FileType.Signature.Type.RAW : FileType.Signature.Type.ASCII;
|
||||
|
||||
/**
|
||||
* Get the signature bytes.
|
||||
*/
|
||||
String sigString = signatureTextField.getText();
|
||||
if (sigString.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.message"),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
byte[] signatureBytes;
|
||||
if (FileType.Signature.Type.RAW == sigType) {
|
||||
try {
|
||||
signatureBytes = DatatypeConverter.parseHexBinary(sigString);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidRawSignatureBytes.message"),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignatureBytes.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
signatureBytes = sigString.getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the offset.
|
||||
*/
|
||||
long offset;
|
||||
try {
|
||||
offset = Long.parseUnsignedLong(offsetTextField.getText());
|
||||
} catch (NumberFormatException ex) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.message"),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interesting files set details.
|
||||
*/
|
||||
String filesSetName = filesSetNameTextField.getText();
|
||||
if (postHitCheckBox.isSelected() && filesSetName.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message"),
|
||||
NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put it all together and reset the file types list component.
|
||||
*/
|
||||
FileType.Signature signature = new FileType.Signature(signatureBytes, offset, sigType);
|
||||
FileType fileType = new FileType(typeName, signature, filesSetName, postHitCheckBox.isSelected());
|
||||
fileTypes.put(typeName, fileType);
|
||||
updateFileTypesListModel();
|
||||
typesList.setSelectedValue(fileType.getMimeType(), true);
|
||||
}//GEN-LAST:event_saveTypeButtonActionPerformed
|
||||
|
||||
private void postHitCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_postHitCheckBoxActionPerformed
|
||||
filesSetNameTextField.setEnabled(postHitCheckBox.isSelected());
|
||||
enableButtons();
|
||||
}//GEN-LAST:event_postHitCheckBoxActionPerformed
|
||||
|
||||
private void signatureTypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_signatureTypeComboBoxActionPerformed
|
||||
hexPrefixLabel.setVisible(signatureTypeComboBox.getSelectedItem() == FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM);
|
||||
}//GEN-LAST:event_signatureTypeComboBoxActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton deleteTypeButton;
|
||||
private javax.swing.JLabel filesSetNameLabel;
|
||||
private javax.swing.JTextField filesSetNameTextField;
|
||||
private javax.swing.JLabel hexPrefixLabel;
|
||||
private javax.swing.JScrollPane hintScrollPane;
|
||||
private javax.swing.JTextArea hintTextArea;
|
||||
private javax.swing.JLabel ingestRunningWarningLabel;
|
||||
private javax.swing.JLabel mimeTypeLabel;
|
||||
private javax.swing.JTextField mimeTypeTextField;
|
||||
private javax.swing.JButton newTypeButton;
|
||||
private javax.swing.JLabel offsetLabel;
|
||||
private javax.swing.JTextField offsetTextField;
|
||||
private javax.swing.JCheckBox postHitCheckBox;
|
||||
private javax.swing.JButton saveTypeButton;
|
||||
private javax.swing.JSeparator separator;
|
||||
private javax.swing.JLabel signatureLabel;
|
||||
private javax.swing.JTextField signatureTextField;
|
||||
private javax.swing.JComboBox<String> signatureTypeComboBox;
|
||||
private javax.swing.JLabel signatureTypeLabel;
|
||||
private javax.swing.JList<String> typesList;
|
||||
private javax.swing.JScrollPane typesScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -18,7 +18,10 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<Component id="skipKnownCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="skipSmallFilesCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="skipKnownCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -28,7 +31,9 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
|
||||
<Component id="skipKnownCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="47" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="skipSmallFilesCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="60" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -38,15 +43,25 @@
|
||||
<Properties>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdModuleSettingsPanel.skipKnownCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdModuleSettingsPanel.skipKnownCheckBox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="skipKnownCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="skipSmallFilesCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdIngestJobSettingsPanel.skipSmallFilesCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="skipSmallFilesCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.filetypeid;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
|
||||
|
||||
@ -25,25 +26,34 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
|
||||
* UI component used to set ingest job options for file type identifier ingest
|
||||
* modules.
|
||||
*/
|
||||
final class FileTypeIdModuleSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
class FileTypeIdIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel {
|
||||
|
||||
private final FileTypeIdModuleSettings settings;
|
||||
|
||||
public FileTypeIdModuleSettingsPanel(FileTypeIdModuleSettings settings) {
|
||||
FileTypeIdIngestJobSettingsPanel(FileTypeIdModuleSettings settings) {
|
||||
this.settings = settings;
|
||||
initComponents();
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
private void customizeComponents() {
|
||||
skipKnownCheckBox.setSelected(settings.skipKnownFiles());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public IngestModuleIngestJobSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does child component initialization in addition to that done by the
|
||||
* Matisse generated code.
|
||||
*/
|
||||
private void customizeComponents() {
|
||||
skipKnownCheckBox.setSelected(settings.skipKnownFiles());
|
||||
skipSmallFilesCheckBox.setSelected(settings.skipSmallFiles());
|
||||
skipSmallFilesCheckBox.setText(NbBundle.getMessage(FileTypeIdIngestJobSettingsPanel.class, "FileTypeIdIngestJobSettingsPanel.skipSmallFilesCheckBox.text", settings.minFileSizeInBytes()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
@ -54,23 +64,33 @@ final class FileTypeIdModuleSettingsPanel extends IngestModuleIngestJobSettingsP
|
||||
private void initComponents() {
|
||||
|
||||
skipKnownCheckBox = new javax.swing.JCheckBox();
|
||||
skipSmallFilesCheckBox = new javax.swing.JCheckBox();
|
||||
|
||||
skipKnownCheckBox.setSelected(true);
|
||||
skipKnownCheckBox.setText(org.openide.util.NbBundle.getMessage(FileTypeIdModuleSettingsPanel.class, "FileTypeIdModuleSettingsPanel.skipKnownCheckBox.text")); // NOI18N
|
||||
skipKnownCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(FileTypeIdModuleSettingsPanel.class, "FileTypeIdModuleSettingsPanel.skipKnownCheckBox.toolTipText")); // NOI18N
|
||||
skipKnownCheckBox.setText(org.openide.util.NbBundle.getMessage(FileTypeIdIngestJobSettingsPanel.class, "FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text")); // NOI18N
|
||||
skipKnownCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(FileTypeIdIngestJobSettingsPanel.class, "FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText")); // NOI18N
|
||||
skipKnownCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
skipKnownCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
skipSmallFilesCheckBox.setText(org.openide.util.NbBundle.getMessage(FileTypeIdIngestJobSettingsPanel.class, "FileTypeIdIngestJobSettingsPanel.skipSmallFilesCheckBox.text")); // NOI18N
|
||||
skipSmallFilesCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
skipSmallFilesCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(10, 10, 10)
|
||||
.addComponent(skipKnownCheckBox)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(skipSmallFilesCheckBox)
|
||||
.addComponent(skipKnownCheckBox))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
@ -78,14 +98,22 @@ final class FileTypeIdModuleSettingsPanel extends IngestModuleIngestJobSettingsP
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(11, 11, 11)
|
||||
.addComponent(skipKnownCheckBox)
|
||||
.addContainerGap(47, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(skipSmallFilesCheckBox)
|
||||
.addContainerGap(60, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void skipKnownCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skipKnownCheckBoxActionPerformed
|
||||
settings.setSkipKnownFiles(skipKnownCheckBox.isSelected());
|
||||
}//GEN-LAST:event_skipKnownCheckBoxActionPerformed
|
||||
|
||||
private void skipSmallFilesCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skipSmallFilesCheckBoxActionPerformed
|
||||
settings.setSkipSmallFiles(skipSmallFilesCheckBox.isSelected());
|
||||
}//GEN-LAST:event_skipSmallFilesCheckBoxActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox skipKnownCheckBox;
|
||||
private javax.swing.JCheckBox skipSmallFilesCheckBox;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -20,127 +20,38 @@ package org.sleuthkit.autopsy.modules.filetypeid;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskData.FileKnown;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
|
||||
/**
|
||||
* Detects the type of a file based on signature (magic) values. Posts results
|
||||
* to the blackboard.
|
||||
*/
|
||||
// TODO: This class does not need to be public.
|
||||
public class FileTypeIdIngestModule implements FileIngestModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FileTypeIdIngestModule.class.getName());
|
||||
private static final long MIN_FILE_SIZE = 512;
|
||||
private final FileTypeIdModuleSettings settings;
|
||||
private long jobId;
|
||||
|
||||
private long jobId;
|
||||
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
|
||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||
private TikaFileTypeDetector tikaDetector = new TikaFileTypeDetector();
|
||||
private final UserDefinedFileTypeIdentifier userDefinedFileTypeIdentifier;
|
||||
private final TikaFileTypeDetector tikaDetector = new TikaFileTypeDetector();
|
||||
|
||||
private static class IngestJobTotals {
|
||||
long matchTime = 0;
|
||||
long numFiles = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the match time total and increment num of files for this job
|
||||
* @param ingestJobId
|
||||
* @param matchTimeInc amount of time to add
|
||||
*/
|
||||
private static synchronized void addToTotals(long ingestJobId, long matchTimeInc) {
|
||||
IngestJobTotals ingestJobTotals = totalsForIngestJobs.get(ingestJobId);
|
||||
if (ingestJobTotals == null) {
|
||||
ingestJobTotals = new IngestJobTotals();
|
||||
totalsForIngestJobs.put(ingestJobId, ingestJobTotals);
|
||||
}
|
||||
|
||||
ingestJobTotals.matchTime += matchTimeInc;
|
||||
ingestJobTotals.numFiles++;
|
||||
totalsForIngestJobs.put(ingestJobId, ingestJobTotals);
|
||||
}
|
||||
|
||||
FileTypeIdIngestModule(FileTypeIdModuleSettings settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||
jobId = context.getJobId();
|
||||
refCounter.incrementAndGet(jobId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessResult process(AbstractFile abstractFile) {
|
||||
// skip non-files
|
||||
if ((abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|
||||
|| (abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) {
|
||||
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
if (settings.skipKnownFiles() && (abstractFile.getKnown() == FileKnown.KNOWN)) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
if (abstractFile.getSize() < MIN_FILE_SIZE) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
tikaDetector.detectAndSave(abstractFile);
|
||||
addToTotals(jobId, (System.currentTimeMillis() - startTime)); //add match time
|
||||
return ProcessResult.OK;
|
||||
} catch (TskException ex) {
|
||||
logger.log(Level.WARNING, "Error matching file signature", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Error matching file signature", e); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutDown() {
|
||||
// We only need to post the summary msg from the last module per job
|
||||
if (refCounter.decrementAndGet(jobId) == 0) {
|
||||
IngestJobTotals jobTotals;
|
||||
synchronized(this) {
|
||||
jobTotals = totalsForIngestJobs.remove(jobId);
|
||||
}
|
||||
if (jobTotals != null) {
|
||||
StringBuilder detailsSb = new StringBuilder();
|
||||
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
|
||||
detailsSb.append("<tr><td>").append(FileTypeIdModuleFactory.getModuleName()).append("</td></tr>"); //NON-NLS
|
||||
detailsSb.append("<tr><td>") //NON-NLS
|
||||
.append(NbBundle.getMessage(this.getClass(), "FileTypeIdIngestModule.complete.totalProcTime"))
|
||||
.append("</td><td>").append(jobTotals.matchTime).append("</td></tr>\n"); //NON-NLS
|
||||
detailsSb.append("<tr><td>") //NON-NLS
|
||||
.append(NbBundle.getMessage(this.getClass(), "FileTypeIdIngestModule.complete.totalFiles"))
|
||||
.append("</td><td>").append(jobTotals.numFiles).append("</td></tr>\n"); //NON-NLS
|
||||
detailsSb.append("</table>"); //NON-NLS
|
||||
IngestServices.getInstance().postMessage(IngestMessage.createMessage(IngestMessage.MessageType.INFO, FileTypeIdModuleFactory.getModuleName(),
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"FileTypeIdIngestModule.complete.srvMsg.text"),
|
||||
detailsSb.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if a given mime type is in the detector's registry.
|
||||
*
|
||||
@ -157,4 +68,157 @@ public class FileTypeIdIngestModule implements FileIngestModule {
|
||||
TikaFileTypeDetector detector = new TikaFileTypeDetector();
|
||||
return detector.isMimeTypeDetectable(mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ingest module that detects the type of a file based on
|
||||
* signature (magic) values. Posts results to the blackboard.
|
||||
*
|
||||
* @param settings The ingest module settings.
|
||||
*/
|
||||
FileTypeIdIngestModule(FileTypeIdModuleSettings settings) {
|
||||
this.settings = settings;
|
||||
userDefinedFileTypeIdentifier = new UserDefinedFileTypeIdentifier();
|
||||
try {
|
||||
userDefinedFileTypeIdentifier.loadFileTypes();
|
||||
} catch (UserDefinedFileTypesManager.UserDefinedFileTypesException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load file types", ex);
|
||||
MessageNotifyUtil.Notify.error(FileTypeIdModuleFactory.getModuleName(), ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||
jobId = context.getJobId();
|
||||
refCounter.incrementAndGet(jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public ProcessResult process(AbstractFile file) {
|
||||
|
||||
String name = file.getName();
|
||||
|
||||
/**
|
||||
* Skip unallocated space and unused blocks files.
|
||||
*/
|
||||
if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|
||||
|| (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) {
|
||||
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip known files if configured to do so.
|
||||
*/
|
||||
if (settings.skipKnownFiles() && (file.getKnown() == FileKnown.KNOWN)) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out very small files to minimize false positives.
|
||||
*/
|
||||
if (settings.skipSmallFiles() && file.getSize() < settings.minFileSizeInBytes()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
FileType fileType = this.userDefinedFileTypeIdentifier.identify(file);
|
||||
if (null != fileType) {
|
||||
String moduleName = FileTypeIdModuleFactory.getModuleName();
|
||||
BlackboardArtifact getInfoArtifact = file.getGenInfoArtifact();
|
||||
BlackboardAttribute typeAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID(), moduleName, fileType.getMimeType());
|
||||
getInfoArtifact.addAttribute(typeAttr);
|
||||
|
||||
if (fileType.alertOnMatch()) {
|
||||
BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
|
||||
BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), moduleName, fileType.getFilesSetName());
|
||||
artifact.addAttribute(setNameAttribute);
|
||||
|
||||
/**
|
||||
* Use the MIME type as the category, i.e., the rule that
|
||||
* determined this file belongs to the interesting files
|
||||
* set.
|
||||
*/
|
||||
BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, fileType.getMimeType());
|
||||
artifact.addAttribute(ruleNameAttribute);
|
||||
}
|
||||
|
||||
} else {
|
||||
tikaDetector.detectAndSave(file);
|
||||
}
|
||||
addToTotals(jobId, (System.currentTimeMillis() - startTime));
|
||||
return ProcessResult.OK;
|
||||
} catch (TskException ex) {
|
||||
logger.log(Level.WARNING, "Error matching file signature", ex); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Error matching file signature", e); //NON-NLS
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void shutDown() {
|
||||
/**
|
||||
* If this is the instance of this module for this ingest job, post a
|
||||
* summary message to the ingest messages box.
|
||||
*/
|
||||
if (refCounter.decrementAndGet(jobId) == 0) {
|
||||
IngestJobTotals jobTotals;
|
||||
synchronized (this) {
|
||||
jobTotals = totalsForIngestJobs.remove(jobId);
|
||||
}
|
||||
if (jobTotals != null) {
|
||||
StringBuilder detailsSb = new StringBuilder();
|
||||
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
|
||||
detailsSb.append("<tr><td>").append(FileTypeIdModuleFactory.getModuleName()).append("</td></tr>"); //NON-NLS
|
||||
detailsSb.append("<tr><td>") //NON-NLS
|
||||
.append(NbBundle.getMessage(this.getClass(), "FileTypeIdIngestModule.complete.totalProcTime"))
|
||||
.append("</td><td>").append(jobTotals.matchTime).append("</td></tr>\n"); //NON-NLS
|
||||
detailsSb.append("<tr><td>") //NON-NLS
|
||||
.append(NbBundle.getMessage(this.getClass(), "FileTypeIdIngestModule.complete.totalFiles"))
|
||||
.append("</td><td>").append(jobTotals.numFiles).append("</td></tr>\n"); //NON-NLS
|
||||
detailsSb.append("</table>"); //NON-NLS
|
||||
IngestServices.getInstance().postMessage(IngestMessage.createMessage(IngestMessage.MessageType.INFO, FileTypeIdModuleFactory.getModuleName(),
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"FileTypeIdIngestModule.complete.srvMsg.text"),
|
||||
detailsSb.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the match time total and increment number of files processed for
|
||||
* this ingest job.
|
||||
*
|
||||
* @param jobId The ingest job identifier.
|
||||
* @param matchTimeInc Amount of time to add.
|
||||
*/
|
||||
private static synchronized void addToTotals(long jobId, long matchTimeInc) {
|
||||
IngestJobTotals ingestJobTotals = totalsForIngestJobs.get(jobId);
|
||||
if (ingestJobTotals == null) {
|
||||
ingestJobTotals = new IngestJobTotals();
|
||||
totalsForIngestJobs.put(jobId, ingestJobTotals);
|
||||
}
|
||||
|
||||
ingestJobTotals.matchTime += matchTimeInc;
|
||||
ingestJobTotals.numFiles++;
|
||||
totalsForIngestJobs.put(jobId, ingestJobTotals);
|
||||
}
|
||||
|
||||
private static class IngestJobTotals {
|
||||
|
||||
long matchTime = 0;
|
||||
long numFiles = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,69 +24,121 @@ import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
|
||||
|
||||
/**
|
||||
* An factory that creates file ingest modules that determine the types of
|
||||
* files.
|
||||
* A factory that creates file ingest modules that determine the types of files.
|
||||
*/
|
||||
// TODO: This class does not need to be public.
|
||||
@ServiceProvider(service = IngestModuleFactory.class)
|
||||
public class FileTypeIdModuleFactory extends IngestModuleFactoryAdapter {
|
||||
|
||||
FileTypeIdGlobalSettingsPanel globalSettingsPanel;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String getModuleDisplayName() {
|
||||
return getModuleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the module display name.
|
||||
*
|
||||
* @return The name string.
|
||||
*/
|
||||
static String getModuleName() {
|
||||
return NbBundle.getMessage(FileTypeIdIngestModule.class,
|
||||
"FileTypeIdIngestModule.moduleName.text");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String getModuleDescription() {
|
||||
return NbBundle.getMessage(FileTypeIdIngestModule.class,
|
||||
"FileTypeIdIngestModule.moduleDesc.text");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String getModuleVersionNumber() {
|
||||
return Version.getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean hasGlobalSettingsPanel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() {
|
||||
if (null == globalSettingsPanel) {
|
||||
globalSettingsPanel = new FileTypeIdGlobalSettingsPanel();
|
||||
}
|
||||
globalSettingsPanel.load();
|
||||
return globalSettingsPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
|
||||
return new FileTypeIdModuleSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean hasIngestJobSettingsPanel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
|
||||
assert settings instanceof FileTypeIdModuleSettings;
|
||||
if (!(settings instanceof FileTypeIdModuleSettings)) {
|
||||
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
|
||||
"FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg"));
|
||||
}
|
||||
return new FileTypeIdModuleSettingsPanel((FileTypeIdModuleSettings) settings);
|
||||
"FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg"));
|
||||
}
|
||||
return new FileTypeIdIngestJobSettingsPanel((FileTypeIdModuleSettings) settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean isFileIngestModuleFactory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
|
||||
assert settings instanceof FileTypeIdModuleSettings;
|
||||
if (!(settings instanceof FileTypeIdModuleSettings)) {
|
||||
throw new IllegalArgumentException(
|
||||
NbBundle.getMessage(this.getClass(), "FileTypeIdModuleFactory.createFileIngestModule.exception.msg"));
|
||||
}
|
||||
}
|
||||
return new FileTypeIdIngestModule((FileTypeIdModuleSettings) settings);
|
||||
}
|
||||
}
|
||||
|
@ -23,23 +23,30 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
/**
|
||||
* Ingest job options for the file type identifier ingest module instances.
|
||||
*/
|
||||
// TODO: This class does not need to be public.
|
||||
public class FileTypeIdModuleSettings implements IngestModuleIngestJobSettings {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private volatile boolean skipKnownFiles = true;
|
||||
private static final long MIN_FILE_SIZE_IN_BYTES = 512;
|
||||
private boolean skipKnownFiles = true;
|
||||
private boolean skipSmallFiles = true;
|
||||
|
||||
FileTypeIdModuleSettings() {
|
||||
}
|
||||
|
||||
FileTypeIdModuleSettings(boolean skipKnownFiles) {
|
||||
FileTypeIdModuleSettings(boolean skipKnownFiles, boolean skipSmallFiles) {
|
||||
this.skipKnownFiles = skipKnownFiles;
|
||||
this.skipSmallFiles = skipSmallFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public long getVersionNumber() {
|
||||
return serialVersionUID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setSkipKnownFiles(boolean enabled) {
|
||||
skipKnownFiles = enabled;
|
||||
}
|
||||
@ -47,4 +54,17 @@ public class FileTypeIdModuleSettings implements IngestModuleIngestJobSettings {
|
||||
boolean skipKnownFiles() {
|
||||
return skipKnownFiles;
|
||||
}
|
||||
}
|
||||
|
||||
void setSkipSmallFiles(boolean enabled) {
|
||||
this.skipSmallFiles = enabled;
|
||||
}
|
||||
|
||||
boolean skipSmallFiles() {
|
||||
return skipSmallFiles;
|
||||
}
|
||||
|
||||
long minFileSizeInBytes() {
|
||||
return MIN_FILE_SIZE_IN_BYTES;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.filetypeid;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import javax.swing.JComponent;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.Lookup;
|
||||
|
||||
@OptionsPanelController.TopLevelRegistration(
|
||||
categoryName = "#OptionsCategory_Name_FileTypeId",
|
||||
iconBase = "org/sleuthkit/autopsy/modules/filetypeid/user-defined-file-types-settings.png",
|
||||
keywords = "#OptionsCategory_Keywords_FileTypeId",
|
||||
keywordsCategory = "FileTypeId"
|
||||
)
|
||||
@org.openide.util.NbBundle.Messages({"OptionsCategory_Name_FileTypeId=FileTypeId", "OptionsCategory_Keywords_FileTypeId=FileTypeId"})
|
||||
public final class FileTypeIdOptionsPanelController extends OptionsPanelController {
|
||||
|
||||
private FileTypeIdGlobalSettingsPanel panel;
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
private boolean changed;
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
getPanel().load();
|
||||
changed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyChanges() {
|
||||
getPanel().store();
|
||||
changed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpCtx getHelpCtx() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent(Lookup masterLookup) {
|
||||
return getPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener l) {
|
||||
pcs.addPropertyChangeListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||
pcs.removePropertyChangeListener(l);
|
||||
}
|
||||
|
||||
private FileTypeIdGlobalSettingsPanel getPanel() {
|
||||
if (panel == null) {
|
||||
panel = new FileTypeIdGlobalSettingsPanel();
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
void changed() {
|
||||
if (!changed) {
|
||||
changed = true;
|
||||
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
|
||||
}
|
||||
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema version="1.0"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xs:simpleType name="stringType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:minLength value="1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="verbatimStringType">
|
||||
<xs:restriction base="stringType">
|
||||
<xs:whiteSpace value="preserve"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="sigInterpretationType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="RAW"/>
|
||||
<xs:enumeration value="ASCII"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="signatureType">
|
||||
<xs:sequence>
|
||||
<xs:element name="Bytes" type="stringType"/>
|
||||
<xs:element name="Offset" type="xs:nonNegativeInteger"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" type="sigInterpretationType" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="fileType">
|
||||
<xs:sequence>
|
||||
<xs:element name="MimeType" type="verbatimStringType"/>
|
||||
<xs:element name="Signature" type="signatureType"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="InterestingFileSset" type="stringType"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="alert" type="xs:boolean" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="FileTypes">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="0" maxOccurs="unbounded" name="FileType" type="fileType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
@ -69,18 +69,6 @@ public class TikaFileTypeDetector {
|
||||
buf = buffer;
|
||||
}
|
||||
|
||||
// the xml detection in Tika tries to parse the entire file and throws exceptions
|
||||
// for files that are not valid XML
|
||||
try {
|
||||
String tagHeader = new String(buf, 0, 5);
|
||||
if (tagHeader.equals("<?xml")) { //NON-NLS
|
||||
return "text/xml"; //NON-NLS
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
String mimetype = tikaInst.detect(buf, abstractFile.getName());
|
||||
// Remove tika's name out of the general types like msoffice and ooxml
|
||||
return mimetype.replace("tika-", ""); //NON-NLS
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.modules.filetypeid;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* Does file type identification for user-defined file types.
|
||||
*/
|
||||
final class UserDefinedFileTypeIdentifier {
|
||||
|
||||
private Map<String, FileType> fileTypes;
|
||||
|
||||
/**
|
||||
* Creates an object that can do file type identification for user-defined
|
||||
* file types.
|
||||
*/
|
||||
UserDefinedFileTypeIdentifier() {
|
||||
fileTypes = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user-defined file types from the user-defined file types
|
||||
* manager.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
void loadFileTypes() throws UserDefinedFileTypesManager.UserDefinedFileTypesException {
|
||||
fileTypes = UserDefinedFileTypesManager.getInstance().getFileTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to identify a file using the set of user-defined file type file
|
||||
* types.
|
||||
*
|
||||
* @param file The file to type.
|
||||
* @return A FileType object or null if identification fails.
|
||||
*/
|
||||
FileType identify(final AbstractFile file) {
|
||||
FileType type = null;
|
||||
for (FileType fileType : this.fileTypes.values()) {
|
||||
if (fileType.matches(file)) {
|
||||
type = fileType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,518 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.modules.filetypeid;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.XMLUtil;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Manages user-defined file types characterized by MIME type, signature, and
|
||||
* optional membership in an interesting files set.
|
||||
* <p>
|
||||
* Note that this class exposes a very simple get/set API that operates on the
|
||||
* user-defined file types as a complete set - there is no concept of adding,
|
||||
* editing or deleting file types singly. This works because this class is not
|
||||
* exposed outside of this ingest module package and is ONLY used in a very
|
||||
* specific paradigm. In this paradigm, there is a single modal writer of file
|
||||
* types in the form of a global settings panel that disables itself when ingest
|
||||
* is running so that multiple readers in the form of file ingest modules get a
|
||||
* consistent set of file type definitions.
|
||||
* <p>
|
||||
* Thread-safe.
|
||||
*/
|
||||
final class UserDefinedFileTypesManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UserDefinedFileTypesManager.class.getName());
|
||||
private static final String FILE_TYPE_DEFINITIONS_SCHEMA_FILE = "FileTypes.xsd"; //NON-NLS
|
||||
private static final String USER_DEFINED_TYPE_DEFINITIONS_FILE = "UserFileTypeDefinitions.xml"; //NON-NLS
|
||||
private static final String FILE_TYPES_TAG_NAME = "FileTypes"; //NON-NLS
|
||||
private static final String FILE_TYPE_TAG_NAME = "FileType"; //NON-NLS
|
||||
private static final String MIME_TYPE_TAG_NAME = "MimeType"; //NON-NLS
|
||||
private static final String SIGNATURE_TAG_NAME = "Signature"; //NON-NLS
|
||||
private static final String SIGNATURE_TYPE_ATTRIBUTE = "type"; //NON-NLS
|
||||
private static final String BYTES_TAG_NAME = "Bytes"; //NON-NLS
|
||||
private static final String OFFSET_TAG_NAME = "Offset"; //NON-NLS
|
||||
private static final String INTERESTING_FILES_SET_TAG_NAME = "InterestingFileSset"; //NON-NLS
|
||||
private static final String ALERT_ATTRIBUTE = "alert"; //NON-NLS
|
||||
private static final String ENCODING_FOR_XML_FILE = "UTF-8"; //NON-NLS
|
||||
private static final String ASCII_ENCODING = "US-ASCII"; //NON-NLS
|
||||
private static UserDefinedFileTypesManager instance;
|
||||
|
||||
/**
|
||||
* File types to be persisted to the user-defined file type definitions file
|
||||
* are stored in this mapping of MIME types to file types. Access to this
|
||||
* map is guarded by the intrinsic lock of the user-defined file types
|
||||
* manager for thread-safety.
|
||||
*/
|
||||
private final Map<String, FileType> userDefinedFileTypes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The combined set of user-defined file types and file types predefined by
|
||||
* Autopsy are stored in this mapping of MIME types to file types. This is
|
||||
* the current working set of file types. Access to this map is guarded by
|
||||
* the intrinsic lock of the user-defined file types manager for
|
||||
* thread-safety.
|
||||
*/
|
||||
private final Map<String, FileType> fileTypes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Gets the singleton manager of user-defined file types characterized by
|
||||
* MIME type, signature, and optional membership in an interesting files
|
||||
* set.
|
||||
*
|
||||
* @return The user-defined file types manager singleton.
|
||||
*/
|
||||
synchronized static UserDefinedFileTypesManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new UserDefinedFileTypesManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a manager of user-defined file types characterized by MIME type,
|
||||
* signature, and optional membership in an interesting files set.
|
||||
*/
|
||||
private UserDefinedFileTypesManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets both the predefined and the user-defined file types.
|
||||
*
|
||||
* @return A mapping of file type names to file types, possibly empty.
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
synchronized Map<String, FileType> getFileTypes() throws UserDefinedFileTypesException {
|
||||
loadFileTypes();
|
||||
|
||||
/**
|
||||
* It is safe to return references to the internal file type objects
|
||||
* because they are immutable. Note that
|
||||
* Collections.unmodifiableCollection() is not used here because this
|
||||
* view of the file types is a snapshot.
|
||||
*/
|
||||
return new HashMap<>(fileTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user-defined file types.
|
||||
*
|
||||
* @return A mapping of file type names to file types, possibly empty.
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
synchronized Map<String, FileType> getUserDefinedFileTypes() throws UserDefinedFileTypesException {
|
||||
loadFileTypes();
|
||||
|
||||
/**
|
||||
* It is safe to return references to the internal file type objects
|
||||
* because they are immutable. Note that
|
||||
* Collections.unmodifiableCollection() is not used here because this
|
||||
* view of the file types is a snapshot.
|
||||
*/
|
||||
return new HashMap<>(userDefinedFileTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the MIME type to file type mappings with predefined and
|
||||
* user-defined types.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
private void loadFileTypes() throws UserDefinedFileTypesException {
|
||||
fileTypes.clear();
|
||||
userDefinedFileTypes.clear();
|
||||
/**
|
||||
* Load the predefined types first so that they can be overwritten by
|
||||
* any user-defined types with the same names.
|
||||
*/
|
||||
loadPredefinedFileTypes();
|
||||
loadUserDefinedFileTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the predefined file types to the in-memory mappings of MIME types to
|
||||
* file types.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
private void loadPredefinedFileTypes() throws UserDefinedFileTypesException {
|
||||
try {
|
||||
FileType fileType = new FileType("text/xml", new Signature("<?xml".getBytes(ASCII_ENCODING), 0L, FileType.Signature.Type.ASCII), "", false);
|
||||
fileTypes.put(fileType.getMimeType(), fileType);
|
||||
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
/**
|
||||
* Using an all-or-none policy.
|
||||
*/
|
||||
fileTypes.clear();
|
||||
throwUserDefinedFileTypesException(ex, "UserDefinedFileTypesManager.loadFileTypes.errorMessage");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the user-defined file types to the in-memory mappings of MIME types
|
||||
* to file types.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
private void loadUserDefinedFileTypes() throws UserDefinedFileTypesException {
|
||||
try {
|
||||
String filePath = getFileTypeDefinitionsFilePath(USER_DEFINED_TYPE_DEFINITIONS_FILE);
|
||||
File file = new File(filePath);
|
||||
if (file.exists() && file.canRead()) {
|
||||
for (FileType fileType : XmlReader.readFileTypes(filePath)) {
|
||||
addUserDefinedFileType(fileType);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException | ParserConfigurationException | SAXException ex) {
|
||||
/**
|
||||
* Using an all-or-none policy.
|
||||
*/
|
||||
fileTypes.clear();
|
||||
userDefinedFileTypes.clear();
|
||||
throwUserDefinedFileTypesException(ex, "UserDefinedFileTypesManager.loadFileTypes.errorMessage");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user-defined file type to the in-memory mappings of MIME types to
|
||||
* file types.
|
||||
*
|
||||
* @param fileType The file type to add.
|
||||
*/
|
||||
private void addUserDefinedFileType(FileType fileType) {
|
||||
userDefinedFileTypes.put(fileType.getMimeType(), fileType);
|
||||
fileTypes.put(fileType.getMimeType(), fileType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user-defined file types.
|
||||
*
|
||||
* @param newFileTypes A mapping of file type names to user-defined file
|
||||
* types.
|
||||
*/
|
||||
synchronized void setUserDefinedFileTypes(Map<String, FileType> newFileTypes) throws UserDefinedFileTypesException {
|
||||
try {
|
||||
String filePath = getFileTypeDefinitionsFilePath(USER_DEFINED_TYPE_DEFINITIONS_FILE);
|
||||
XmlWriter.writeFileTypes(newFileTypes.values(), filePath);
|
||||
} catch (ParserConfigurationException | FileNotFoundException | UnsupportedEncodingException | TransformerException ex) {
|
||||
throwUserDefinedFileTypesException(ex, "UserDefinedFileTypesManager.saveFileTypes.errorMessage");
|
||||
} catch (IOException ex) {
|
||||
throwUserDefinedFileTypesException(ex, "UserDefinedFileTypesManager.saveFileTypes.errorMessage");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the absolute path of a file type definitions file.
|
||||
*
|
||||
* @param fileName The name of the file.
|
||||
* @return The absolute path to the file.
|
||||
*/
|
||||
private static String getFileTypeDefinitionsFilePath(String fileName) {
|
||||
Path filePath = Paths.get(PlatformUtil.getUserConfigDirectory(), fileName);
|
||||
return filePath.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a mechanism for writing a set of file type definitions to an XML
|
||||
* file.
|
||||
*/
|
||||
private static class XmlWriter {
|
||||
|
||||
/**
|
||||
* Writes a set of file type definitions to an XML file.
|
||||
*
|
||||
* @param fileTypes A collection of file types.
|
||||
* @param filePath The path to the destination file.
|
||||
* @throws ParserConfigurationException
|
||||
* @throws IOException
|
||||
* @throws FileNotFoundException
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws TransformerException
|
||||
*/
|
||||
private static void writeFileTypes(Collection<FileType> fileTypes, String filePath) throws ParserConfigurationException, IOException, FileNotFoundException, UnsupportedEncodingException, TransformerException {
|
||||
Document doc = XMLUtil.createDocument();
|
||||
Element fileTypesElem = doc.createElement(FILE_TYPES_TAG_NAME);
|
||||
doc.appendChild(fileTypesElem);
|
||||
for (FileType fileType : fileTypes) {
|
||||
Element fileTypeElem = XmlWriter.createFileTypeElement(fileType, doc);
|
||||
fileTypesElem.appendChild(fileTypeElem);
|
||||
}
|
||||
XMLUtil.saveDocument(doc, ENCODING_FOR_XML_FILE, filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an XML representation of a file type.
|
||||
*
|
||||
* @param fileType The file type object.
|
||||
* @param doc The WC3 DOM object to use to create the XML.
|
||||
* @return An XML element.
|
||||
*/
|
||||
private static Element createFileTypeElement(FileType fileType, Document doc) {
|
||||
Element fileTypeElem = doc.createElement(FILE_TYPE_TAG_NAME);
|
||||
XmlWriter.addMimeTypeElement(fileType, fileTypeElem, doc);
|
||||
XmlWriter.addSignatureElement(fileType, fileTypeElem, doc);
|
||||
XmlWriter.addInterestingFilesSetElement(fileType, fileTypeElem, doc);
|
||||
XmlWriter.addAlertAttribute(fileType, fileTypeElem);
|
||||
return fileTypeElem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a MIME type child element to a file type XML element.
|
||||
*
|
||||
* @param fileType The file type to use as a content source.
|
||||
* @param fileTypeElem The parent file type element.
|
||||
* @param doc The WC3 DOM object to use to create the XML.
|
||||
*/
|
||||
private static void addMimeTypeElement(FileType fileType, Element fileTypeElem, Document doc) {
|
||||
Element typeNameElem = doc.createElement(MIME_TYPE_TAG_NAME);
|
||||
typeNameElem.setTextContent(fileType.getMimeType());
|
||||
fileTypeElem.appendChild(typeNameElem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a signature child element to a file type XML element.
|
||||
*
|
||||
* @param fileType The file type to use as a content source.
|
||||
* @param fileTypeElem The parent file type element.
|
||||
* @param doc The WC3 DOM object to use to create the XML.
|
||||
*/
|
||||
private static void addSignatureElement(FileType fileType, Element fileTypeElem, Document doc) {
|
||||
Signature signature = fileType.getSignature();
|
||||
Element signatureElem = doc.createElement(SIGNATURE_TAG_NAME);
|
||||
|
||||
Element bytesElem = doc.createElement(BYTES_TAG_NAME);
|
||||
bytesElem.setTextContent(DatatypeConverter.printHexBinary(signature.getSignatureBytes()));
|
||||
signatureElem.appendChild(bytesElem);
|
||||
|
||||
Element offsetElem = doc.createElement(OFFSET_TAG_NAME);
|
||||
offsetElem.setTextContent(DatatypeConverter.printLong(signature.getOffset()));
|
||||
signatureElem.appendChild(offsetElem);
|
||||
|
||||
signatureElem.setAttribute(SIGNATURE_TYPE_ATTRIBUTE, signature.getType().toString());
|
||||
fileTypeElem.appendChild(signatureElem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an interesting files set element to a file type XML element.
|
||||
*
|
||||
* @param fileType The file type to use as a content source.
|
||||
* @param fileTypeElem The parent file type element.
|
||||
* @param doc The WC3 DOM object to use to create the XML.
|
||||
*/
|
||||
private static void addInterestingFilesSetElement(FileType fileType, Element fileTypeElem, Document doc) {
|
||||
if (!fileType.getFilesSetName().isEmpty()) {
|
||||
Element filesSetElem = doc.createElement(INTERESTING_FILES_SET_TAG_NAME);
|
||||
filesSetElem.setTextContent(fileType.getFilesSetName());
|
||||
fileTypeElem.appendChild(filesSetElem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alert attribute to a file type XML element.
|
||||
*
|
||||
* @param fileType The file type to use as a content source.
|
||||
* @param fileTypeElem The parent file type element.
|
||||
*/
|
||||
private static void addAlertAttribute(FileType fileType, Element fileTypeElem) {
|
||||
fileTypeElem.setAttribute(ALERT_ATTRIBUTE, Boolean.toString(fileType.alertOnMatch()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a mechanism for reading a set of file type definitions from an
|
||||
* XML file.
|
||||
*/
|
||||
private static class XmlReader {
|
||||
|
||||
/**
|
||||
* Reads a set of file type definitions from an XML file.
|
||||
*
|
||||
* @param filePath The path to the XML file.
|
||||
* @return A collection of file types read from the XML file.
|
||||
*/
|
||||
private static List<FileType> readFileTypes(String filePath) throws IOException, ParserConfigurationException, SAXException {
|
||||
List<FileType> fileTypes = new ArrayList<>();
|
||||
Document doc = XMLUtil.loadDocument(filePath, UserDefinedFileTypesManager.class, FILE_TYPE_DEFINITIONS_SCHEMA_FILE);
|
||||
if (doc != null) {
|
||||
Element fileTypesElem = doc.getDocumentElement();
|
||||
if (fileTypesElem != null && fileTypesElem.getNodeName().equals(FILE_TYPES_TAG_NAME)) {
|
||||
NodeList fileTypeElems = fileTypesElem.getElementsByTagName(FILE_TYPE_TAG_NAME);
|
||||
for (int i = 0; i < fileTypeElems.getLength(); ++i) {
|
||||
Element fileTypeElem = (Element) fileTypeElems.item(i);
|
||||
FileType fileType = XmlReader.parseFileType(fileTypeElem);
|
||||
fileTypes.add(fileType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file type definition from a file type XML element.
|
||||
*
|
||||
* @param fileTypeElem The XML element.
|
||||
* @return A file type object.
|
||||
* @throws IllegalArgumentException
|
||||
* @throws NumberFormatException
|
||||
*/
|
||||
private static FileType parseFileType(Element fileTypeElem) throws IllegalArgumentException, NumberFormatException {
|
||||
String mimeType = XmlReader.parseMimeType(fileTypeElem);
|
||||
Signature signature = XmlReader.parseSignature(fileTypeElem);
|
||||
String filesSetName = XmlReader.parseInterestingFilesSet(fileTypeElem);
|
||||
boolean alert = XmlReader.parseAlert(fileTypeElem);
|
||||
return new FileType(mimeType, signature, filesSetName, alert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MIME type from a file type XML element.
|
||||
*
|
||||
* @param fileTypeElem The element
|
||||
* @return A MIME type string.
|
||||
*/
|
||||
private static String parseMimeType(Element fileTypeElem) {
|
||||
return getChildElementTextContent(fileTypeElem, MIME_TYPE_TAG_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the signature from a file type XML element.
|
||||
*
|
||||
* @param fileTypeElem The XML element.
|
||||
* @return The signature.
|
||||
*/
|
||||
private static Signature parseSignature(Element fileTypeElem) throws IllegalArgumentException, NumberFormatException {
|
||||
NodeList signatureElems = fileTypeElem.getElementsByTagName(SIGNATURE_TAG_NAME);
|
||||
Element signatureElem = (Element) signatureElems.item(0);
|
||||
|
||||
String sigTypeAttribute = signatureElem.getAttribute(SIGNATURE_TYPE_ATTRIBUTE);
|
||||
Signature.Type signatureType = Signature.Type.valueOf(sigTypeAttribute);
|
||||
|
||||
String sigBytesString = getChildElementTextContent(signatureElem, BYTES_TAG_NAME);
|
||||
byte[] signatureBytes = DatatypeConverter.parseHexBinary(sigBytesString);
|
||||
|
||||
String offsetString = getChildElementTextContent(signatureElem, OFFSET_TAG_NAME);
|
||||
long offset = DatatypeConverter.parseLong(offsetString);
|
||||
|
||||
return new Signature(signatureBytes, offset, signatureType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interesting files set name from a file type XML element.
|
||||
*
|
||||
* @param fileTypeElem The XML element.
|
||||
* @return The files set name, possibly empty.
|
||||
*/
|
||||
private static String parseInterestingFilesSet(Element fileTypeElem) {
|
||||
String filesSetName = "";
|
||||
NodeList filesSetElems = fileTypeElem.getElementsByTagName(INTERESTING_FILES_SET_TAG_NAME);
|
||||
if (filesSetElems.getLength() > 0) {
|
||||
Element filesSetElem = (Element) filesSetElems.item(0);
|
||||
filesSetName = filesSetElem.getTextContent();
|
||||
}
|
||||
return filesSetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alert attribute from a file type XML element.
|
||||
*
|
||||
* @param fileTypeElem The XML element.
|
||||
* @return True or false;
|
||||
*/
|
||||
private static boolean parseAlert(Element fileTypeElem) {
|
||||
String alertAttribute = fileTypeElem.getAttribute(ALERT_ATTRIBUTE);
|
||||
return Boolean.parseBoolean(alertAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text content of a single child element.
|
||||
*
|
||||
* @param elem The parent element.
|
||||
* @param tagName The tag name of the child element.
|
||||
* @return The text content.
|
||||
*/
|
||||
private static String getChildElementTextContent(Element elem, String tagName) {
|
||||
NodeList childElems = elem.getElementsByTagName(tagName);
|
||||
Element childElem = (Element) childElems.item(0);
|
||||
return childElem.getTextContent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an exception, bundles the exception with a simple message in a
|
||||
* uniform exception type, and throws the wrapper exception.
|
||||
*
|
||||
* @param ex The exception to wrap.
|
||||
* @param messageKey A key into the bundle file that maps to the desired
|
||||
* message.
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
|
||||
*/
|
||||
private void throwUserDefinedFileTypesException(Exception ex, String messageKey) throws UserDefinedFileTypesException {
|
||||
String message = NbBundle.getMessage(UserDefinedFileTypesManager.class, messageKey);
|
||||
logger.log(Level.SEVERE, message, ex);
|
||||
throw new UserDefinedFileTypesException(message, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to translate more implementation-details-specific exceptions (which
|
||||
* are logged by this class) into more generic exceptions for propagation to
|
||||
* clients of the user-defined file types manager.
|
||||
*/
|
||||
static class UserDefinedFileTypesException extends Exception {
|
||||
|
||||
UserDefinedFileTypesException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
UserDefinedFileTypesException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
Core/src/org/sleuthkit/autopsy/modules/filetypeid/warning16.png
Normal file
BIN
Core/src/org/sleuthkit/autopsy/modules/filetypeid/warning16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 552 B |
Loading…
x
Reference in New Issue
Block a user