Merge pull request #1007 from rcordovano/file_typing_module_enhancements

File typing module enhancements
This commit is contained in:
Richard Cordovano 2014-12-22 22:05:28 -05:00
commit b1b37e2e08
19 changed files with 2405 additions and 170 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View 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;
}
}
}
}

View File

@ -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="&lt;String&gt;"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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="&lt;String&gt;"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -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
}

View File

@ -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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties" key="FileTypeIdIngestJobSettingsPanel.skipKnownCheckBox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="skipSmallFilesCheckBoxActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B