From a333d97c05e1cfa815b8530cae945f49692872c5 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sat, 29 Nov 2014 14:09:28 -0500 Subject: [PATCH] Continue work on user defined file types feature --- .../sleuthkit/autopsy/coreutils/XMLUtil.java | 12 + .../autopsy/modules/filetypeid/FileType.java | 152 ++++++++ .../filetypeid/FileTypeIdSettingsPanel.java | 60 ++-- .../UserDefinedFileTypeIdentifier.java | 49 +-- .../filetypeid/UserDefinedFileTypes.java | 338 +++++++++++++++++- .../filetypeid/UserDefinedFileTypes.xsd | 50 +++ 6 files changed, 594 insertions(+), 67 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java create mode 100644 Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.xsd diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java index a997f01dd5..dc12e3bd2c 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java @@ -54,6 +54,18 @@ import org.xml.sax.SAXException; */ public class XMLUtil { + /** + * Creates a W3C DOM document. + * + * @return The document object. + * @throws ParserConfigurationException + */ + public static Document createDoc() throws ParserConfigurationException { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + return builder.newDocument(); + } + /** * Utility to validate XML files against pre-defined schema files. * diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java new file mode 100644 index 0000000000..d4fc484673 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java @@ -0,0 +1,152 @@ +/* + * 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.util.Arrays; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Represents a named file type characterized by a file signature. + */ +class FileType { + + private final String typeName; + private final Signature signature; + private final boolean alert; + + /** + * Creates a representation of a named file type characterized by a file + * signature. + * + * @param typeName The name of the file type. + * @param signature The signature that characterizes the file type. + * @param alert A flag indicating whether the user wishes to be alerted when + * a file matching this type is encountered. + */ + FileType(String typeName, Signature signature, boolean alert) { + this.typeName = typeName; + this.signature = signature; + this.alert = alert; + } + + /** + * Gets the name associated with this file type. + * + * @return The type name. + */ + String getTypeName() { + return this.typeName; + } + + /** + * Gets the signature associated with this file type. + * + * @return The file signature. + */ + Signature getSignature() { + return this.signature; + } + + /** + * Determines whether or not a given file is an instance of this file type. + * + * @param file The file to test + * @return True or false. + */ + boolean matches(AbstractFile file) { + return this.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 this.alert; + } + + /** + * Represents a file signature consisting of a sequence of bytes at a + * specific offset within a file. + */ + static class Signature { + + /** + * 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 representation of 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(byte[] signatureBytes, long offset, Type type) { + this.signatureBytes = signatureBytes; + 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(this.signatureBytes, this.signatureBytes.length); + } + + /** + * Gets the offset of the signature. + * + * @return The offset. + */ + long getOffset() { + return this.offset; + } + + /** + * Gets the interpretation of the byte sequence for the signature. + * + * @return The signature type. + */ + Type getType() { + return this.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(AbstractFile file) { + try { + byte[] buffer = new byte[this.signatureBytes.length]; + int bytesRead = file.read(buffer, offset, this.signatureBytes.length); + return ((bytesRead == this.signatureBytes.length) && (Arrays.equals(buffer, this.signatureBytes))); + } catch (TskCoreException ex) { + return false; + } + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdSettingsPanel.java index af160cd19b..ec6598b838 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdSettingsPanel.java @@ -18,27 +18,28 @@ */ package org.sleuthkit.autopsy.modules.filetypeid; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.swing.DefaultListModel; import javax.swing.ListModel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; +//import org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypes.FileType; +import org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypes.UserDefinedFileTypesException; /** * A panel to allow a user to make custom file type definitions. */ final class FileTypeIdSettingsPanel extends IngestModuleGlobalSettingsPanel { - -// private final HashMap fileTypeDefs; + + private final HashMap fileTypes; // private final ListModel fileTypesListModel; /** * Creates a panel to allow a user to make custom file type definitions. */ FileTypeIdSettingsPanel() { - this.fileTypeDefs = new HashMap<>(); + this.fileTypes = new HashMap<>(); initComponents(); } @@ -49,41 +50,44 @@ final class FileTypeIdSettingsPanel extends IngestModuleGlobalSettingsPanel { public void saveSettings() { this.store(); } - + /** * RJCTODO */ void load() { -// try { -// this.fileTypeDefs.clear(); -// DefaultListModel fileTypesListModel = new DefaultListModel<>(); -// for (FileTypeDefinition fileTypeDef : FileTypeDefinitionsManager.getFileTypeDefinitions()) { -// this.fileTypeDefs.put(fileTypeDef.getTypeName(), fileTypeDef); -// fileTypesListModel.addElement(fileTypeDef.getTypeName()); -// } -// this.typesList.setModel(fileTypesListModel); -// } catch (IOException ex) { -// // RJCTODO -// } + try { + this.fileTypes.clear(); + DefaultListModel fileTypesListModel = new DefaultListModel<>(); + for (FileType fileType : UserDefinedFileTypes.getFileTypes()) { + this.fileTypes.put(fileType.getTypeName(), fileType); + fileTypesListModel.addElement(fileType.getTypeName()); + } + this.typesList.setModel(fileTypesListModel); + } catch (UserDefinedFileTypesException ex) { + // RJCTODO + } } /** * RJCTODO */ void store() { -// try { -// ListModel fileTypesListModel = this.typesList.getModel(); -// List newFileTypeDefs = new ArrayList<>(); -// for (int i = 0; i < fileTypesListModel.getSize(); ++i) { -// String typeName = fileTypesListModel.getElementAt(i); -// newFileTypeDefs.add(this.fileTypeDefs.get(typeName)); -// } -// FileTypeDefinitionsManager.setFileTypeDefinitions(newFileTypeDefs); -// } catch (IOException ex) { -// // RJCTODO -// } + try { + ListModel fileTypesListModel = this.typesList.getModel(); + List newFileTypes = new ArrayList<>(); + for (int i = 0; i < fileTypesListModel.getSize(); ++i) { + String typeName = fileTypesListModel.getElementAt(i); + newFileTypes.add(this.fileTypes.get(typeName)); + } + UserDefinedFileTypes.setFileTypes(newFileTypes); + } catch (UserDefinedFileTypesException ex) { + // RJCTODO + } } - + + /** + * RJCTODO + */ boolean valid() { // RJCTODO return true; diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java index 5e9e7047c1..5a5ad78f31 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java @@ -23,55 +23,40 @@ import java.util.List; import org.sleuthkit.datamodel.AbstractFile; /** - * Does file type identification with user-defined file type fileTypeDefs. + * Does file type identification with user-defined file types. */ -class UserDefinedFileTypeIdentifier { +final class UserDefinedFileTypeIdentifier { - private final List fileTypeDefs; + private final List fileTypes; /** * Creates an object that does file type identification with user-defined - file type fileTypeDefs. - * - * @param sigFilePath A path to a fileTypeDef definitions file. + * file types. Does not load the file type definitions. */ - static UserDefinedFileTypeIdentifier createDetector(String sigFilePath) { - UserDefinedFileTypeIdentifier detector = new UserDefinedFileTypeIdentifier(); - detector.loadSignatures(sigFilePath); - return detector; + UserDefinedFileTypeIdentifier() { + this.fileTypes = new ArrayList<>(); } /** - * Create an object that does file type identification with user-defined - file type fileTypeDefs. + * Loads the set of user-defined file types. */ - private UserDefinedFileTypeIdentifier() { - this.fileTypeDefs = new ArrayList<>(); - } - - /** - * Loads a set of user-defined file type fileTypeDefs from a file. - * - * @param sigFilePath The path to the fileTypeDef definitions file. - */ - private void loadSignatures(String sigFilePath) { - // RJCTODO: Load fileTypeDef file, creating + void loadFileTypes() throws UserDefinedFileTypes.UserDefinedFileTypesException { + this.fileTypes.clear(); + this.fileTypes.addAll(UserDefinedFileTypes.getFileTypes()); } /** * Attempts to identify a file using the set of user-defined file type - fileTypeDefs. + * file types. * * @param file The file to type. - * @return A MIME type string or the empty string if identification fails. + * @return A FileType object or null if identification fails. */ - String identify(AbstractFile file) { - String type = ""; - for (FileTypeDefinitionsManager.FileTypeDefinition fileTypeDef : this.fileTypeDefs) { - if (fileTypeDef.matches(file)) { - type = fileTypeDef.getTypeName(); - // RJCTODO: Add attribute to GEN IBNFO artifact? - // RJCTODO: Handle alert here? + FileType identify(AbstractFile file) { + FileType type = null; + for (FileType fileType : this.fileTypes) { + if (fileType.matches(file)) { + type = fileType; break; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.java index 4c2ca9044f..84bc4f8778 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.java @@ -1,14 +1,338 @@ /* - * 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. + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +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 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.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; + /** - * - * @author rcordovano + * Allows a user to define named file types characterized by file signatures. */ -public class UserDefinedFileTypes { - +final class UserDefinedFileTypes { + + private static final Logger logger = Logger.getLogger(UserDefinedFileTypes.class.getName()); + private static final String FILE_TYPE_DEFINITIONS_SCHEMA_FILE = "FileTypeDefinitions.xsd"; // NON-NLS + private static final String USER_DEFINED_TYPE_DEFINITIONS_FILE = "UserFileTypeDefinitions.xml"; // NON-NLS + private static final String AUTOPSY_TYPE_DEFINITIONS_FILE = "AutopsyFileTypeDefinitions.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 ALERT_ATTRIBUTE = "alert"; // NON-NLS + private static final String TYPE_NAME_TAG_NAME = "typename"; // 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 ENCODING = "UTF-8"; //NON-NLS // RJCTODO: Is this right? + + static { + UserDefinedFileTypes.writePredefinedTypes(); + } + + /** + * Writes the predefined file types definition file. + */ + private static void writePredefinedTypes() { + try { + Path filePath = Paths.get(PlatformUtil.getUserConfigDirectory(), UserDefinedFileTypes.USER_DEFINED_TYPE_DEFINITIONS_FILE); + String filePathString = filePath.toAbsolutePath().toString(); + List fileTypes = new ArrayList<>(); // RJCTODO + UserDefinedFileTypes.writeFileTypes(fileTypes, filePathString); + } catch (UserDefinedFileTypesException ex) { + // RJCTODO + } + } + + /** + * Gets the user-defined file types. + * + * @return A list of file types, possibly empty + */ + synchronized static List getFileTypes() throws UserDefinedFileTypesException { + Map fileTypes = new HashMap<>(); + UserDefinedFileTypes.readFileTypes(fileTypes, UserDefinedFileTypes.AUTOPSY_TYPE_DEFINITIONS_FILE); + UserDefinedFileTypes.readFileTypes(fileTypes, UserDefinedFileTypes.USER_DEFINED_TYPE_DEFINITIONS_FILE); + return new ArrayList(fileTypes.values()); + } + + /** + * Reads file type definitions from a file into a map of type names to file + * types. File types already loaded into the map will be overwritten by file + * types from the file if the type name is the same. + * + * @param fileTypes The map of type names to file type objects. + * @param fileName The file from which to read the file type definitions. + */ + private static void readFileTypes(Map fileTypes, String fileName) throws UserDefinedFileTypesException { + Path filePath = Paths.get(PlatformUtil.getUserConfigDirectory(), fileName); + String filePathString = filePath.toAbsolutePath().toString(); + File file = new File(filePathString); + if (file.exists() && file.canRead()) { + for (FileType fileType : UserDefinedFileTypes.XMLReader.readFileTypes(filePathString)) { + fileTypes.put(fileType.getTypeName(), fileType); + } + } + } + + /** + * Sets the user-defined file types. + * + * @param fileTypes The file type definitions. + * @throws UserDefinedFileTypesException + */ + synchronized static void setFileTypes(List fileTypes) throws UserDefinedFileTypesException { + Path filePath = Paths.get(PlatformUtil.getUserConfigDirectory(), UserDefinedFileTypes.USER_DEFINED_TYPE_DEFINITIONS_FILE); + String filePathString = filePath.toAbsolutePath().toString(); + UserDefinedFileTypes.writeFileTypes(fileTypes, filePathString); + } + + /** + * Writes a set of file type definitions to a given file. + * + * @param fileTypes The file types. + * @param filePaht The destination file. + * @throws UserDefinedFileTypesException + */ + private static void writeFileTypes(List fileTypes, String filePath) throws UserDefinedFileTypesException { + try { + UserDefinedFileTypes.XMLWriter.writeFileTypes(fileTypes, filePath); + } catch (ParserConfigurationException | IOException ex) { + UserDefinedFileTypes.logger.log(Level.SEVERE, "Failed to write file types file", ex); + throw new UserDefinedFileTypesException(ex.getLocalizedMessage()); // RJCTODO: Create a bundled message + } + } + + /** + * 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 signatures A collection of file types. + * @param filePath The path to the destination file. + */ + private static void writeFileTypes(List fileTypes, String filePath) throws ParserConfigurationException, IOException { + Document doc = XMLUtil.createDoc(); + Element fileTypesElem = doc.createElement(UserDefinedFileTypes.FILE_TYPES_TAG_NAME); + doc.appendChild(fileTypesElem); + for (FileType fileType : fileTypes) { + Element fileTypeElem = UserDefinedFileTypes.XMLWriter.createFileTypeElement(fileType, doc); + fileTypesElem.appendChild(fileTypeElem); + } + if (!XMLUtil.saveDoc(HashDbManager.class, filePath, UserDefinedFileTypes.ENCODING, doc)) { + throw new IOException("Could not save user defined file types"); + } + } + + /** + * Creates an XML representation of a file type. + * + * @param fileType The file type object. + * @param doc The DOM document to use to create the XML. + * @return An XML element. + */ + private static Element createFileTypeElement(FileType fileType, Document doc) { + /** + * Create a file type element with an alert attribute. + */ + Element fileTypeElem = doc.createElement(UserDefinedFileTypes.FILE_TYPE_TAG_NAME); + fileTypeElem.setAttribute(UserDefinedFileTypes.ALERT_ATTRIBUTE, Boolean.toString(fileType.alertOnMatch())); + + /** + * Add a type name child element. + */ + Element typeNameElem = doc.createElement(UserDefinedFileTypes.TYPE_NAME_TAG_NAME); + typeNameElem.setTextContent(fileType.getTypeName()); + fileTypeElem.appendChild(typeNameElem); + + /** + * Add a signature child element with a type attribute. + */ + Signature signature = fileType.getSignature(); + Element signatureElem = doc.createElement(UserDefinedFileTypes.SIGNATURE_TAG_NAME); + signatureElem.setAttribute(UserDefinedFileTypes.SIGNATURE_TYPE_ATTRIBUTE, signature.getType().toString()); + fileTypeElem.appendChild(signatureElem); + + /** + * Add a bytes child element to the signature element. + */ + Element bytesElem = doc.createElement(UserDefinedFileTypes.BYTES_TAG_NAME); + bytesElem.setTextContent(DatatypeConverter.printHexBinary(signature.getSignatureBytes())); + signatureElem.appendChild(bytesElem); + + /** + * Add an offset child element to the signature element. + */ + Element offsetElem = doc.createElement(UserDefinedFileTypes.OFFSET_TAG_NAME); + offsetElem.setTextContent(DatatypeConverter.printLong(signature.getOffset())); + signatureElem.appendChild(offsetElem); + + return fileTypeElem; + } + } + + /** + * 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 file. + * @return A collection of file types. + */ + private static List readFileTypes(String filePath) throws UserDefinedFileTypesException { + List fileTypes = new ArrayList<>(); + Path xsdPath = Paths.get(PlatformUtil.getUserConfigDirectory(), UserDefinedFileTypes.FILE_TYPE_DEFINITIONS_SCHEMA_FILE); + String xsdPathString = xsdPath.toAbsolutePath().toString(); + File file = new File(xsdPathString); + if (file.exists() && file.canRead()) { + Document doc = XMLUtil.loadDoc(UserDefinedFileTypes.XMLReader.class, filePath, xsdPathString); + if (doc != null) { + Element fileTypesElem = doc.getDocumentElement(); + if (fileTypesElem != null && fileTypesElem.getNodeName().equals(UserDefinedFileTypes.FILE_TYPES_TAG_NAME)) { + NodeList fileTypeElems = fileTypesElem.getElementsByTagName(UserDefinedFileTypes.FILE_TYPE_TAG_NAME); + for (int i = 0; i < fileTypeElems.getLength(); ++i) { + Element fileTypeElem = (Element) fileTypeElems.item(i); + FileType fileType = UserDefinedFileTypes.XMLReader.parseFileType(fileTypeElem); + fileTypes.add(fileType); + } + } + } + } + return fileTypes; + } + + /** + * Parse a file type definition from a file type XML element. + * + * @param fileTypeElem The XML element. + * @return A file type object. + * @throws UserDefinedFileTypesException + */ + private static FileType parseFileType(Element fileTypeElem) throws UserDefinedFileTypesException { + /** + * Get the alert attribute. + */ + String alertAttribute = fileTypeElem.getAttribute(UserDefinedFileTypes.ALERT_ATTRIBUTE); + boolean alert = Boolean.parseBoolean(alertAttribute); + + /** + * Get the type name child element. + */ + String typeName = UserDefinedFileTypes.getChildElementTextContent(fileTypeElem, UserDefinedFileTypes.TYPE_NAME_TAG_NAME); + + /** + * Get the signature child element. + */ + Element signatureElem; + NodeList signatureElems = fileTypeElem.getElementsByTagName(UserDefinedFileTypes.SIGNATURE_TAG_NAME); + signatureElem = (Element) signatureElems.item(0); + + /** + * Get the signature type attribute from the signature child + * element. + */ + String sigTypeAttribute = signatureElem.getAttribute(typeName); + Signature.Type signatureType = Signature.Type.valueOf(sigTypeAttribute); + + /** + * Get the signature bytes. + */ + String sigBytesString = UserDefinedFileTypes.getChildElementTextContent(signatureElem, UserDefinedFileTypes.TYPE_NAME_TAG_NAME); + byte[] signatureBytes = DatatypeConverter.parseHexBinary(sigBytesString); + + /** + * Get the offset. + */ + String offsetString = UserDefinedFileTypes.getChildElementTextContent(signatureElem, UserDefinedFileTypes.OFFSET_TAG_NAME); + long offset = DatatypeConverter.parseLong(offsetString); + + /** + * Put it all together. + */ + Signature signature = new Signature(signatureBytes, offset, signatureType); + return new FileType(typeName, signature, alert); + } + } + + /** + * 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. + * @throws UserDefinedFileTypesException + */ + private static String getChildElementTextContent(Element elem, String tagName) throws UserDefinedFileTypesException { + /** + * The checks here are essentially "sanity checks" since the XML was + * already validated using the XSD file. + */ + String textContent = ""; + NodeList childElems = elem.getElementsByTagName(tagName); + if (childElems.getLength() > 0) { + Element childElem = (Element) childElems.item(0); + textContent = childElem.getTextContent(); + if (textContent.isEmpty()) { + UserDefinedFileTypes.logger.log(Level.SEVERE, "File type {0} child element missing text content", tagName); // NON-NLS + } + } else { + UserDefinedFileTypes.logger.log(Level.SEVERE, "File type element missing {0} child element", tagName); // NON-NLS + } + if (textContent.isEmpty()) { + throw new UserDefinedFileTypes.UserDefinedFileTypesException("Error reading file type definitions file, see log for details"); // RJCTODO: Bundle + } + return textContent; + } + + /** + * An exception type to conflate more implementation-details-specific + * exception types (which are logged by this class) into a single generic + * type. + */ + static class UserDefinedFileTypesException extends Exception { + + UserDefinedFileTypesException(String message) { + super(message); + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.xsd b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.xsd new file mode 100644 index 0000000000..2512542294 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypes.xsd @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +