diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties
index 4481e95ab3..2d18364283 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties
@@ -9,7 +9,6 @@ FileTypeIdModuleFactory.createFileIngestModule.exception.msg=Expected settings a
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.saveTypeButton.text=Save Type
FileTypeIdGlobalSettingsPanel.deleteTypeButton.text=DeleteType
FileTypeIdGlobalSettingsPanel.newTypeButton.text=New Type
FileTypeIdGlobalSettingsPanel.offsetTextField.text=
@@ -21,3 +20,4 @@ FileTypeIdGlobalSettingsPanel.signatureTypeLabel.text=Signature Type
FileTypeIdGlobalSettingsPanel.mimeTypeTextField.text=
FileTypeIdGlobalSettingsPanel.signatureLabel.text=Signature
FileTypeIdGlobalSettingsPanel.mimeTypeLabel.text=Mime Type
+FileTypeIdGlobalSettingsPanel.saveTypeButton.text=Save Type
diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form
index fd6d2250d3..49ae296a57 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form
@@ -35,7 +35,6 @@
-
@@ -43,18 +42,17 @@
-
-
+
+
-
-
+
@@ -78,22 +76,17 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
@@ -141,14 +134,14 @@
-
+
-
+
@@ -167,6 +160,9 @@
+
+
+
@@ -223,6 +219,9 @@
+
+
+
@@ -230,6 +229,9 @@
+
+
+
@@ -237,6 +239,9 @@
+
+
+
@@ -248,13 +253,12 @@
-
-
-
-
-
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java
index e9b0060653..aaafa786eb 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java
@@ -18,23 +18,30 @@
*/
package org.sleuthkit.autopsy.modules.filetypeid;
-import java.util.HashMap;
+import java.util.Map;
+import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
+import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
+import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
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.
*/
final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
- private final HashMap fileTypes = new HashMap<>();
- private final HashMap changedFileTypes = new HashMap<>();
- private final HashMap deletedFileTypes = new HashMap<>();
+ private static final String RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM = "Bytes (Hex)"; // RJCTODO: Bundle
+ private static final String ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM = "String (ASCII)"; // RJCTODO: Bundle
+
+ /**
+ * This mapping of file type names to file types is used to hold the types
+ * displayed in the file types list component.
+ */
+ private Map fileTypes;
/**
* Creates a panel to allow a user to make custom file type definitions.
@@ -56,19 +63,38 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
*/
@Override
public void load() {
- fileTypes.clear();
+ /**
+ * Get the user-defined file types and set up a list model for the file
+ * types list component.
+ */
+ this.fileTypes = UserDefinedFileTypesManager.getInstance().getUserDefinedFileTypes();
DefaultListModel fileTypesListModel = new DefaultListModel<>();
- for (FileType fileType : UserDefinedFileTypesManager.getInstance().getFileTypes()) {
- fileTypes.put(fileType.getTypeName(), fileType);
+ for (FileType fileType : this.fileTypes.values()) {
fileTypesListModel.addElement(fileType.getTypeName());
}
- typesList.setModel(fileTypesListModel);
+ this.typesList.setModel(fileTypesListModel);
- typesList.addListSelectionListener(new TypesListSelectionListener());
+ /**
+ * Add a selection listener to populate the file type details
+ * display/edit components.
+ */
+ this.typesList.addListSelectionListener(new TypesListSelectionListener());
+ /**
+ * If there is at least one user-defined file type, select it the file
+ * types list component.
+ */
if (!fileTypesListModel.isEmpty()) {
- typesList.setSelectedIndex(0);
+ this.typesList.setSelectedIndex(0);
}
+
+ /**
+ * Make a model for the signature type combo box component.
+ */
+ DefaultComboBoxModel sigTypeComboBoxModel = new DefaultComboBoxModel<>();
+ sigTypeComboBoxModel.addElement(FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM);
+ sigTypeComboBoxModel.addElement(FileTypeIdGlobalSettingsPanel.ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM);
+ this.signatureTypeComboBox.setModel(sigTypeComboBoxModel);
}
/**
@@ -77,10 +103,8 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
@Override
public void store() {
try {
- UserDefinedFileTypesManager fileTypesManager = UserDefinedFileTypesManager.getInstance();
- fileTypesManager.addFileTypes(changedFileTypes.values());
- fileTypesManager.deleteFileTypes(deletedFileTypes.values());
- } catch (UserDefinedFileTypesException ex) {
+ UserDefinedFileTypesManager.getInstance().setUserDefinedFileTypes(this.fileTypes);
+ } catch (UserDefinedFileTypesManager.UserDefinedFileTypesException ex) {
// RJCTODO
}
}
@@ -90,22 +114,36 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
- if (typesList.getSelectedIndex() == -1) {
- deleteTypeButton.setEnabled(false);
-
+ if (FileTypeIdGlobalSettingsPanel.this.typesList.getSelectedIndex() == -1) {
+ FileTypeIdGlobalSettingsPanel.this.deleteTypeButton.setEnabled(false);
} else {
- String typeName = (String) typesList.getSelectedValue();
- FileType fileType = fileTypes.get(typeName);
+ String typeName = FileTypeIdGlobalSettingsPanel.this.typesList.getSelectedValue();
+ FileType fileType = FileTypeIdGlobalSettingsPanel.this.fileTypes.get(typeName);
Signature signature = fileType.getSignature();
- mimeTypeTextField.setText(typeName);
- offsetTextField.setText(Long.toString(signature.getOffset()));
- deleteTypeButton.setEnabled(true);
+ FileTypeIdGlobalSettingsPanel.this.mimeTypeTextField.setText(typeName);
+ FileType.Signature.Type sigType = fileType.getSignature().getType();
+ FileTypeIdGlobalSettingsPanel.this.signatureTypeComboBox.setSelectedItem(sigType == FileType.Signature.Type.RAW ? FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM : FileTypeIdGlobalSettingsPanel.ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM);
+ FileTypeIdGlobalSettingsPanel.this.offsetTextField.setText(Long.toString(signature.getOffset()));
+ FileTypeIdGlobalSettingsPanel.this.postHitCheckBox.setSelected(fileType.alertOnMatch());
+ FileTypeIdGlobalSettingsPanel.this.deleteTypeButton.setEnabled(true);
}
}
}
}
- // RJCTODO: Consider fixing OptinsPanel interface or writing story
+ /**
+ * RJCTODO
+ */
+ private void clearTypeDetailsComponents() {
+ this.typesList.setSelectedIndex(-1);
+ this.mimeTypeTextField.setText(""); //NON-NLS // RJCTODO: Default name?
+ this.signatureTypeComboBox.setSelectedItem(FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM);
+ this.signatureTextField.setText(""); //NON-NLS
+ this.offsetTextField.setText(""); //NON-NLS
+ this.postHitCheckBox.setSelected(false);
+ }
+
+ // RJCTODO: Consider fixing OptionsPanel interface or writing story
/**
* 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
@@ -117,7 +155,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
buttonGroup1 = new javax.swing.ButtonGroup();
typesScrollPane = new javax.swing.JScrollPane();
- typesList = new javax.swing.JList();
+ typesList = new javax.swing.JList();
jSeparator1 = new javax.swing.JSeparator();
mimeTypeLabel = new javax.swing.JLabel();
mimeTypeTextField = new javax.swing.JTextField();
@@ -129,7 +167,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
deleteTypeButton = new javax.swing.JButton();
saveTypeButton = new javax.swing.JButton();
hexPrefixLabel = new javax.swing.JLabel();
- signatureTypeComboBox = new javax.swing.JComboBox();
+ signatureTypeComboBox = new javax.swing.JComboBox();
signatureLabel = new javax.swing.JLabel();
jScrollPane2 = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea();
@@ -152,15 +190,28 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
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.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Bytes (Hex)", "String", " " }));
-
org.openide.awt.Mnemonics.setLocalizedText(signatureLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureLabel.text")); // NOI18N
jTextArea1.setEditable(false);
@@ -190,25 +241,23 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(37, 37, 37)
- .addComponent(saveTypeButton)
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addComponent(saveTypeButton))
.addGroup(layout.createSequentialGroup()
.addGap(18, 18, 18)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
- .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addComponent(signatureTypeLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(0, 0, Short.MAX_VALUE))
+ .addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(signatureLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(hexPrefixLabel)
.addGap(2, 2, 2))
.addGroup(layout.createSequentialGroup()
@@ -224,15 +273,12 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(mimeTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 181, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addGroup(layout.createSequentialGroup()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addGap(6, 6, 6)
- .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 260, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addGroup(layout.createSequentialGroup()
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(postHitCheckBox)))
- .addGap(0, 0, Short.MAX_VALUE)))
- .addContainerGap())))
+ .addGap(6, 6, 6)
+ .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 260, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(postHitCheckBox)))))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -270,13 +316,50 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
.addComponent(newTypeButton)
.addComponent(deleteTypeButton)))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(saveTypeButton))))
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 260, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
);
}// //GEN-END:initComponents
+ private void newTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTypeButtonActionPerformed
+ this.clearTypeDetailsComponents();
+ // RJCTODO: Default name?
+ }//GEN-LAST:event_newTypeButtonActionPerformed
+
+ private void deleteTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTypeButtonActionPerformed
+ String typeName = this.typesList.getSelectedValue();
+ this.fileTypes.remove(typeName);
+ this.clearTypeDetailsComponents();
+ this.typesList.setSelectedIndex(-1);
+ }//GEN-LAST:event_deleteTypeButtonActionPerformed
+
+ private void saveTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveTypeButtonActionPerformed
+ try {
+ String typeName = this.mimeTypeTextField.getText();
+ if (typeName.isEmpty()) {
+ JOptionPane.showMessageDialog(null, "MIME type name cannot be empty.", "Missing MIME Type", JOptionPane.ERROR_MESSAGE); // RJCTODO: Bundle
+ return;
+ }
+
+ String sigString = this.signatureTextField.getText();
+ if (sigString.isEmpty()) {
+ JOptionPane.showMessageDialog(null, "Signature cannot be empty.", "Missing Signature", JOptionPane.ERROR_MESSAGE); // RJCTODO: Bundle
+ return;
+ }
+
+ long offset = Long.parseUnsignedLong(this.offsetTextField.getText());
+
+ FileType.Signature.Type sigType = this.signatureTypeComboBox.getSelectedItem() == FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM ? FileType.Signature.Type.RAW : FileType.Signature.Type.ASCII;
+ FileType.Signature signature = new FileType.Signature(new byte[1], offset, sigType); // RJCTODO:
+ FileType fileType = new FileType(typeName, signature, this.postHitCheckBox.isSelected());
+ this.fileTypes.put(typeName, fileType);
+ } catch (NumberFormatException ex) {
+ JOptionPane.showMessageDialog(null, "Offset is not a positive integer.", "Invalid Offset", JOptionPane.ERROR_MESSAGE); // RJCTODO: Bundle
+ }
+ }//GEN-LAST:event_saveTypeButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup buttonGroup1;
@@ -294,9 +377,9 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
private javax.swing.JButton saveTypeButton;
private javax.swing.JLabel signatureLabel;
private javax.swing.JTextField signatureTextField;
- private javax.swing.JComboBox signatureTypeComboBox;
+ private javax.swing.JComboBox signatureTypeComboBox;
private javax.swing.JLabel signatureTypeLabel;
- private javax.swing.JList typesList;
+ private javax.swing.JList typesList;
private javax.swing.JScrollPane typesScrollPane;
// End of variables declaration//GEN-END:variables
diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java
index f7dac840ca..c93cc9a547 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypeIdentifier.java
@@ -18,8 +18,7 @@
*/
package org.sleuthkit.autopsy.modules.filetypeid;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
import org.sleuthkit.datamodel.AbstractFile;
/**
@@ -27,16 +26,14 @@ import org.sleuthkit.datamodel.AbstractFile;
*/
final class UserDefinedFileTypeIdentifier {
- private final List fileTypes;
+ private final Map fileTypes;
/**
* Creates an object that can do file type identification for user-defined
* file types.
*/
UserDefinedFileTypeIdentifier() {
- this.fileTypes = new ArrayList<>();
- List fileTypeDefs = UserDefinedFileTypesManager.getInstance().getFileTypes();
- this.fileTypes.addAll(fileTypeDefs);
+ this.fileTypes = UserDefinedFileTypesManager.getInstance().getFileTypes();
}
/**
@@ -48,7 +45,7 @@ final class UserDefinedFileTypeIdentifier {
*/
FileType identify(final AbstractFile file) {
FileType type = null;
- for (FileType fileType : this.fileTypes) {
+ for (FileType fileType : this.fileTypes.values()) {
if (fileType.matches(file)) {
type = fileType;
break;
diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java
index c3f177a4c9..1266d3560f 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java
@@ -62,6 +62,13 @@ final class UserDefinedFileTypesManager {
private static final String ASCII_ENCODING = "US-ASCII"; //NON-NLS
private static UserDefinedFileTypesManager instance;
+ /**
+ * Predefined file types are stored in this mapping of file type names 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 predefinedFileTypes = new HashMap<>();
+
/**
* User-defined file types to be persisted to the user-defined file type
* definitions file are stored in this mapping of file type names to file
@@ -96,6 +103,10 @@ final class UserDefinedFileTypesManager {
* (e.g., MIME type) and signatures.
*/
private UserDefinedFileTypesManager() {
+ /**
+ * Load the predefined types first so that they can be overwritten by
+ * any user-defined types with the same names.
+ */
loadPredefinedFileTypes();
loadUserDefinedFileTypes();
}
@@ -109,14 +120,16 @@ final class UserDefinedFileTypesManager {
/**
* Create a file type that should match $MBR in Small2 image.
*/
- this.fileTypes.put("predefinedRAW", new FileType("predefinedRAW", new Signature(new byte[]{(byte) 0x66, (byte) 0x73, (byte) 0x00}, 8L, FileType.Signature.Type.RAW), true));
+ FileType fileType = new FileType("predefinedRAW", new Signature(new byte[]{(byte) 0x66, (byte) 0x73, (byte) 0x00}, 8L, FileType.Signature.Type.RAW), true);
+ this.addPredefinedFileType(fileType);
/**
* Create a file type that should match test.txt in the Small2 image.
*/
// RJCTODO: Remove test type
try {
- this.fileTypes.put("predefinedASCII", new FileType("predefinedASCII", new Signature("hello".getBytes(UserDefinedFileTypesManager.ASCII_ENCODING), 0L, FileType.Signature.Type.ASCII), true));
+ fileType = new FileType("predefinedASCII", new Signature("hello".getBytes(UserDefinedFileTypesManager.ASCII_ENCODING), 0L, FileType.Signature.Type.ASCII), true);
+ this.addPredefinedFileType(fileType);
} catch (UnsupportedEncodingException ex) {
UserDefinedFileTypesManager.logger.log(Level.SEVERE, "Unable to create 'predefinedASCII' predefined file type definition", ex); //NON-NLS
}
@@ -144,16 +157,28 @@ final class UserDefinedFileTypesManager {
// catch (IndexOutOfBoundsException e) {
// // do nothing
// }
- this.fileTypes.put("text/xml", new FileType("text/xml", new Signature(" getFileTypes() {
+ synchronized Map getFileTypes() {
/**
* It is safe to return references to the internal file type objects
* because they are immutable.
*/
- return new ArrayList<>(this.fileTypes.values());
+ return new HashMap<>(this.fileTypes);
}
/**
- * Adds a new user-defined file type, overwriting any existing file type
- * with the same type name.
+ * Gets the user-defined file types.
*
- * @param fileType The file type to add.
- * @throws
- * org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
+ * @return A mapping of file type names to file types, possibly empty.
*/
- synchronized void addFileType(FileType fileType) throws UserDefinedFileTypesException {
- this.addFileTypes(Collections.singletonList(fileType));
- }
-
- /**
- * Adds a collection of new user-defined file types, overwriting any
- * existing file types with the same type names.
- *
- * @param newFileTypes The file types to add.
- * @throws
- * org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
- */
- synchronized void addFileTypes(Collection newFileTypes) throws UserDefinedFileTypesException {
+ synchronized Map getUserDefinedFileTypes() {
/**
- * It is safe to hold references to client-constructed file type objects
+ * It is safe to return references to the internal file type objects
* because they are immutable.
*/
- for (FileType fileType : newFileTypes) {
- this.userDefinedFileTypes.put(fileType.getTypeName(), fileType);
- this.fileTypes.put(fileType.getTypeName(), fileType);
- }
- this.saveUserDefinedTypes();
+ return new HashMap<>(this.userDefinedFileTypes);
}
/**
- * Deletes a user-defined file type.
+ * Sets the user-defined file types.
*
- * @param fileType The file type to delete.
+ * @param newFileTypes A mapping of file type names to user-defined
+ * file types.
* @throws
* org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
*/
- synchronized void deleteFileType(FileType fileType) throws UserDefinedFileTypesException {
- this.deleteFileTypes(Collections.singletonList(fileType));
- }
-
- /**
- * Deletes a set of user-defined file types.
- *
- * @param deletedFileTypes The file types to delete.
- * @throws
- * org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
- */
- synchronized void deleteFileTypes(Collection deletedFileTypes) throws UserDefinedFileTypesException {
- for (FileType fileType : deletedFileTypes) {
- this.userDefinedFileTypes.remove(fileType.getTypeName(), fileType);
- this.fileTypes.remove(fileType.getTypeName(), fileType);
- }
- this.saveUserDefinedTypes();
- }
-
- /**
- * Persists the user-defined file type definitions.
- *
- * @throws
- * org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException
- */
- private void saveUserDefinedTypes() throws UserDefinedFileTypesException {
- String filePath = UserDefinedFileTypesManager.getFileTypeDefinitionsFilePath(UserDefinedFileTypesManager.USER_DEFINED_TYPE_DEFINITIONS_FILE);
- UserDefinedFileTypesManager.writeFileTypes(this.userDefinedFileTypes.values(), filePath);
- }
-
- /**
- * Writes a set of file type definitions to a given file.
- *
- * @param fileTypes The file types.
- * @param filePath The destination file.
- * @throws UserDefinedFileTypesException
- */
- private static void writeFileTypes(Collection fileTypes, String filePath) throws UserDefinedFileTypesException {
+ synchronized void setUserDefinedFileTypes(Map newFileTypes) throws UserDefinedFileTypesManager.UserDefinedFileTypesException {
try {
- UserDefinedFileTypesManager.XMLWriter.writeFileTypes(fileTypes, filePath);
+ /**
+ * Persist the user-defined file type definitions.
+ */
+ String filePath = UserDefinedFileTypesManager.getFileTypeDefinitionsFilePath(UserDefinedFileTypesManager.USER_DEFINED_TYPE_DEFINITIONS_FILE);
+ UserDefinedFileTypesManager.XMLWriter.writeFileTypes(newFileTypes.values(), filePath);
+
} catch (ParserConfigurationException | IOException ex) {
UserDefinedFileTypesManager.logger.log(Level.SEVERE, "Failed to write file types file", ex);
throw new UserDefinedFileTypesManager.UserDefinedFileTypesException(ex.getLocalizedMessage()); // RJCTODO: Create a bundled message
}
+
+ /**
+ * Clear and reinitialize the user-defined file type map. It is safe to
+ * hold references to file type objects obtained for the caller because
+ * they are immutable.
+ */
+ this.userDefinedFileTypes.clear();
+ this.userDefinedFileTypes.putAll(newFileTypes);
+
+ /**
+ * Clear and reinitialize the combined file type map, loading the
+ * predefined types first so that they can be overwritten by any
+ * user-defined types with the same names.
+ */
+ this.fileTypes.clear();
+ this.fileTypes.putAll(this.predefinedFileTypes);
+ this.fileTypes.putAll(this.userDefinedFileTypes);
}
/**