diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java new file mode 100755 index 0000000000..3365d33eb3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java @@ -0,0 +1,196 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.ingest.RunIngestModulesDialog; +import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; + +/** + * A dialog box that allows a user to create a file type signature, to be added + * to a selected file type. + */ +final class AddFileTypeSignatureDialog extends JDialog { + + private static final long serialVersionUID = 1L; + private final AddFileTypeSignaturePanel addFileTypeSigPanel; + private static final String TITLE = NbBundle.getMessage(RunIngestModulesDialog.class, "IngestDialog.title.text"); + private Signature signature; + private BUTTON_PRESSED result; + + /** + * Enum used for letting creator of this dialog know whether or not OK was + * pressed. + */ + enum BUTTON_PRESSED { + + OK, CANCEL; + } + + /** + * Creates a file type signature dialog for a new signature. + */ + AddFileTypeSignatureDialog() { + super(new JFrame(TITLE), TITLE, true); + this.addFileTypeSigPanel = new AddFileTypeSignaturePanel(); + this.display(true); + } + + /** + * Creates a file type signature dialog for a signature being edited. + * + * @param toEdit The signature to edit. + */ + AddFileTypeSignatureDialog(Signature toEdit) { + super(new JFrame(TITLE), TITLE, true); + this.addFileTypeSigPanel = new AddFileTypeSignaturePanel(toEdit); + this.display(false); + } + + /** + * Gets the signature that was created by this dialog. + * + * @return the signature. + */ + public Signature getSignature() { + return signature; + } + + /** + * Gets which button was pressed (OK or Cancel). + * + * @return The result. + */ + public BUTTON_PRESSED getResult() { + return result; + } + + /** + * Displays the add signature dialog. + * + * @param add Whether or not this is an edit or a new window. + */ + @Messages({ + "AddFileTypeSignatureDialog.addButton.title=Add", + "AddFileTypeSignatureDialog.addButton.title2=Done", + "AddFileTypeSignatureDialog.cancelButton.title=Cancel"}) + void display(boolean add) { + setLayout(new BorderLayout()); + + /** + * Center the dialog. + */ + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + int width = this.getSize().width; + int height = this.getSize().height; + setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2); + + /** + * Get the default or saved ingest job settings for this context and use + * them to create and add an ingest job settings panel. + */ + add(this.addFileTypeSigPanel, BorderLayout.PAGE_START); + + // Add the add/done button. + JButton addButton; + if (add) { + addButton = new JButton(Bundle.AddFileTypeSignatureDialog_addButton_title()); + } else { + addButton = new JButton(Bundle.AddFileTypeSignatureDialog_addButton_title2()); + } + addButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doButtonAction(true); + } + }); + + // Add a close button. + JButton closeButton = new JButton(Bundle.AddFileTypeSignatureDialog_cancelButton_title()); + closeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doButtonAction(false); + } + }); + + // Put the buttons in their own panel, under the settings panel. + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS)); + buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10))); + buttonPanel.add(addButton); + buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10))); + buttonPanel.add(closeButton); + add(buttonPanel, BorderLayout.LINE_START); + + /** + * Add a handler for when the dialog window is closed directly, + * bypassing the buttons. + */ + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + doButtonAction(false); + } + }); + + /** + * Show the dialog. + */ + pack(); + setResizable(false); + setVisible(true); + } + + /** + * Performs actions on the fields based on whether the ok button was pressed + * or not. + * + * @param okPressed Whether ok was pressed. + */ + @Messages({"AddFileTypeSignatureDialog.invalidSignature.message=Invalid signature"}) + private void doButtonAction(boolean okPressed) { + if (okPressed) { + Signature sig = addFileTypeSigPanel.getSignature(); + if (sig != null) { + this.signature = sig; + this.result = BUTTON_PRESSED.OK; + setVisible(false); + } + } else { + this.signature = null; + this.result = BUTTON_PRESSED.CANCEL; + setVisible(false); + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.form b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.form new file mode 100755 index 0000000000..ec7f274f55 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.form @@ -0,0 +1,202 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java new file mode 100755 index 0000000000..98d8d5e963 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java @@ -0,0 +1,325 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.UnsupportedEncodingException; +import java.nio.charset.Charset; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JOptionPane; +import javax.xml.bind.DatatypeConverter; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; + +/** + * Panel for creating a file type signature to be added to a file type. + */ +class AddFileTypeSignaturePanel extends javax.swing.JPanel { + + private static final String RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem"); + private static final String START_OFFSET_RELATIVE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetComboBox.startItem"); + private static final String END_OFFSET_RELATIVE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetComboBox.endItem"); + private static final String ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem"); + + /** + * Creates a panel for a new signature. + */ + AddFileTypeSignaturePanel() { + initComponents(); + customizeComponents(); + } + + /** + * Creates a panel for a signature being edited. + * + * @param toEdit The signature to edit. + */ + AddFileTypeSignaturePanel(Signature toEdit) { + this(); + this.setComponentValues(toEdit); + } + + /** + * Configures the components of the panel correctly. + */ + private void customizeComponents() { + setSignatureTypeComboBoxModel(); + setOffsetRealtiveToComboBoxModel(); + } + + /** + * Sets the model for the signature type combo box. + */ + private void setSignatureTypeComboBoxModel() { + DefaultComboBoxModel sigTypeComboBoxModel = new DefaultComboBoxModel<>(); + sigTypeComboBoxModel.addElement(RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM); + sigTypeComboBoxModel.addElement(ASCII_SIGNATURE_TYPE_COMBO_BOX_ITEM); + signatureTypeComboBox.setModel(sigTypeComboBoxModel); + signatureTypeComboBox.setSelectedItem(RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM); + } + + /** + * Sets the model for the signature type combo box. + */ + private void setOffsetRealtiveToComboBoxModel() { + DefaultComboBoxModel offsetRelComboBoxModel = new DefaultComboBoxModel<>(); + offsetRelComboBoxModel.addElement(START_OFFSET_RELATIVE_COMBO_BOX_ITEM); + offsetRelComboBoxModel.addElement(END_OFFSET_RELATIVE_COMBO_BOX_ITEM); + offsetRelativeToComboBox.setModel(offsetRelComboBoxModel); + offsetRelativeToComboBox.setSelectedItem(START_OFFSET_RELATIVE_COMBO_BOX_ITEM); + } + + /** + * Sets the values for the components based on the signature being edited. + * + * @param toEdit The signature information to implement. + */ + @Messages({"AddFileTypeSignaturePanel.signatureStringFail.text=Couldn't get signatures string"}) + private void setComponentValues(Signature toEdit) { + if (toEdit.isRelativeToStart()) { + this.offsetRelativeToComboBox.setSelectedIndex(0); + } else { + this.offsetRelativeToComboBox.setSelectedIndex(1); + } + this.offsetTextField.setText(toEdit.getOffset() + ""); + if (Signature.Type.RAW == toEdit.getType()) { + this.signatureTypeComboBox.setSelectedIndex(0); + this.signatureTextField.setText(DatatypeConverter.printHexBinary(toEdit.getSignatureBytes())); + } else { + this.signatureTypeComboBox.setSelectedIndex(1); + try { + this.signatureTextField.setText(new String(toEdit.getSignatureBytes(), "UTF-8")); + } catch (UnsupportedEncodingException ex) { + JOptionPane.showMessageDialog(null, + ex.getLocalizedMessage(), + Bundle.AddFileTypeSignaturePanel_signatureStringFail_text(), + JOptionPane.ERROR_MESSAGE); + } + } + } + + /** + * Gets the file type signature for this panel, pops up error windows if the + * signature is incomplete. + * + * @return The signature of this panel, or null if it is an invalid + * signature. + */ + public Signature getSignature() { + + /** + * Get the signature type. + */ + FileType.Signature.Type sigType = signatureTypeComboBox.getSelectedItem() == 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 null; + } + byte[] signatureBytes; + if (FileType.Signature.Type.RAW == sigType) { + try { + sigString = sigString.replaceAll("\\s", ""); //NON-NLS + 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 null; + } + } else { + signatureBytes = sigString.getBytes(Charset.forName("UTF-8")); + } + + /** + * Get the offset. + */ + long offset; + boolean isRelativeToStart = offsetRelativeToComboBox.getSelectedItem() == START_OFFSET_RELATIVE_COMBO_BOX_ITEM; + + try { + offset = Long.parseUnsignedLong(offsetTextField.getText()); + if (!isRelativeToStart && signatureBytes.length > offset + 1) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.length"), + NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title"), + JOptionPane.ERROR_MESSAGE); + return null; + } + } 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 null; + } + + /** + * Get the interesting files set details. + */ + /** + * Put it all together and reset the file types list component. + */ + FileType.Signature signature = new FileType.Signature(signatureBytes, offset, sigType, isRelativeToStart); + return signature; + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + offsetLabel = new javax.swing.JLabel(); + offsetTextField = new javax.swing.JTextField(); + offsetRelativeToComboBox = new javax.swing.JComboBox(); + offsetRelativeToLabel = new javax.swing.JLabel(); + hexPrefixLabel = new javax.swing.JLabel(); + signatureTypeComboBox = new javax.swing.JComboBox(); + signatureLabel = new javax.swing.JLabel(); + signatureTypeLabel = new javax.swing.JLabel(); + signatureTextField = new javax.swing.JTextField(); + + offsetLabel.setFont(offsetLabel.getFont().deriveFont(offsetLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + org.openide.awt.Mnemonics.setLocalizedText(offsetLabel, org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.offsetLabel.text")); // NOI18N + + offsetTextField.setFont(offsetTextField.getFont().deriveFont(offsetTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + offsetTextField.setText(org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.offsetTextField.text")); // NOI18N + + offsetRelativeToComboBox.setFont(offsetRelativeToComboBox.getFont().deriveFont(offsetRelativeToComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + + offsetRelativeToLabel.setFont(offsetRelativeToLabel.getFont().deriveFont(offsetRelativeToLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + org.openide.awt.Mnemonics.setLocalizedText(offsetRelativeToLabel, org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.offsetRelativeToLabel.text")); // NOI18N + + hexPrefixLabel.setFont(hexPrefixLabel.getFont().deriveFont(hexPrefixLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + org.openide.awt.Mnemonics.setLocalizedText(hexPrefixLabel, org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.hexPrefixLabel.text")); // NOI18N + + signatureTypeComboBox.setFont(signatureTypeComboBox.getFont().deriveFont(signatureTypeComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + signatureTypeComboBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + signatureTypeComboBoxActionPerformed(evt); + } + }); + + signatureLabel.setFont(signatureLabel.getFont().deriveFont(signatureLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + org.openide.awt.Mnemonics.setLocalizedText(signatureLabel, org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.signatureLabel.text")); // NOI18N + + signatureTypeLabel.setFont(signatureTypeLabel.getFont().deriveFont(signatureTypeLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + org.openide.awt.Mnemonics.setLocalizedText(signatureTypeLabel, org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.signatureTypeLabel.text")); // NOI18N + + signatureTextField.setFont(signatureTextField.getFont().deriveFont(signatureTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + signatureTextField.setText(org.openide.util.NbBundle.getMessage(AddFileTypeSignaturePanel.class, "AddFileTypeSignaturePanel.signatureTextField.text")); // NOI18N + signatureTextField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + signatureTextFieldActionPerformed(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() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(signatureTypeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(signatureLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(hexPrefixLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(signatureTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)) + .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(layout.createSequentialGroup() + .addComponent(offsetRelativeToLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(offsetRelativeToComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(26, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(signatureTypeLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(hexPrefixLabel) + .addComponent(signatureTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(signatureLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(offsetLabel) + .addComponent(offsetTextField, 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(offsetRelativeToComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(offsetRelativeToLabel)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void signatureTypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_signatureTypeComboBoxActionPerformed + if (signatureTypeComboBox.getSelectedItem() == RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM) { + hexPrefixLabel.setVisible(true); + signatureTextField.setText("0000"); + } else { + hexPrefixLabel.setVisible(false); + signatureTextField.setText(""); + } + }//GEN-LAST:event_signatureTypeComboBoxActionPerformed + + private void signatureTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_signatureTextFieldActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_signatureTextFieldActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel hexPrefixLabel; + private javax.swing.JLabel offsetLabel; + private javax.swing.JComboBox offsetRelativeToComboBox; + private javax.swing.JLabel offsetRelativeToLabel; + private javax.swing.JTextField offsetTextField; + private javax.swing.JLabel signatureLabel; + private javax.swing.JTextField signatureTextField; + private javax.swing.JComboBox signatureTypeComboBox; + private javax.swing.JLabel signatureTypeLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties index ccc1bd0aff..d99cf4e0ac 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties @@ -10,17 +10,11 @@ FileTypeIdModuleFactory.getIngestJobSettingsPanel.exception.msg=Expected setting FileTypeIdModuleFactory.createFileIngestModule.exception.msg=Expected settings argument to be instanceof FileTypeIdModuleSettings 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=Delete -FileTypeIdGlobalSettingsPanel.offsetTextField.text= -FileTypeIdGlobalSettingsPanel.offsetLabel.text=Byte Offset FileTypeIdGlobalSettingsPanel.postHitCheckBox.text=Alert as an "Interesting File" 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 +FileTypeIdGlobalSettingsPanel.saveTypeButton.text=Add FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem=Bytes (Hex) FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem=String (ASCII) FileTypeIdGlobalSettingsPanel.offsetComboBox.startItem=Start @@ -48,4 +42,13 @@ FileTypeIdGlobalSettingsPanel.jLabel2.text=Custom MIME Types: FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy can automatically detect many file types. Add your custom file types here. FileTypeIdGlobalSettingsPanel.startUp.fileTypeDetectorInitializationException.msg=Error initializing the file type detector. FileTypeIdIngestModule.startUp.fileTypeDetectorInitializationException.msg=Error initializing the file type detector. -FileTypeIdGlobalSettingsPanel.offsetRelativeToLabel.text=Offset is relative to +AddFileTypeSignaturePanel.offsetLabel.text=Byte Offset +AddFileTypeSignaturePanel.signatureTextField.text= +AddFileTypeSignaturePanel.signatureTypeLabel.text=Signature Type +AddFileTypeSignaturePanel.signatureLabel.text=Signature +AddFileTypeSignaturePanel.hexPrefixLabel.text=0x +AddFileTypeSignaturePanel.offsetRelativeToLabel.text=Offset is relative to +AddFileTypeSignaturePanel.offsetTextField.text= +FileTypeIdGlobalSettingsPanel.deleteSigButton.text=Delete Signature +FileTypeIdGlobalSettingsPanel.addSigButton.text=Add Signature +FileTypeIdGlobalSettingsPanel.editSigButton.text=Edit Signature diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties index a5f9b02e62..863a345b54 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle_ja.properties @@ -24,13 +24,10 @@ FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title=\u8aad\u307f\u8fbc\u3 FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title=\u4fdd\u5b58\u304c\u5931\u6557\u3057\u307e\u3057\u305f FileTypeIdGlobalSettingsPanel.mimeTypeLabel.text=MIME\u30bf\u30a4\u30d7 FileTypeIdGlobalSettingsPanel.newTypeButton.text=\u65b0\u898f\u30bf\u30a4\u30d7 -FileTypeIdGlobalSettingsPanel.offsetLabel.text=\u30d0\u30a4\u30c8\u30aa\u30d5\u30bb\u30c3\u30c8 FileTypeIdGlobalSettingsPanel.postHitCheckBox.text=\u767a\u898b\u3057\u305f\u969b\u306b\u300c\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u300d\u3068\u8b66\u544a FileTypeIdGlobalSettingsPanel.saveTypeButton.text=\u4fdd\u5b58\u30bf\u30a4\u30d7 FileTypeIdGlobalSettingsPanel.signatureComboBox.asciiItem=\u30b9\u30c8\u30ea\u30f3\u30b0\uff08ASCII\uff09 FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem=\u30d0\u30a4\u30c8\uff08HEX\uff09 -FileTypeIdGlobalSettingsPanel.signatureLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3 -FileTypeIdGlobalSettingsPanel.signatureTypeLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3\u30bf\u30a4\u30d7 OptionsCategory_Keywords_FileTypeId=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7ID OptionsCategory_Name_FileTypeId=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 UserDefinedFileTypesManager.loadFileTypes.errorMessage=\u65e2\u5b58\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u5b9a\u7fa9\u306e\u8aad\u307f\u8fbc\u307f\u306b\u5931\u6557\u3057\u307e\u3057\u305f @@ -43,4 +40,7 @@ FileTypeIdGlobalSettingsPanel.jLabel2.text=MIME\u30bf\u30a4\u30d7\uff1a FileTypeIdGlobalSettingsPanel.jLabel3.text=Autopsy\u306f\u81ea\u52d5\u7684\u306b\u591a\u304f\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u691c\u77e5\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u306b\u306f\u3042\u306a\u305f\u306e\u30ab\u30b9\u30bf\u30e0\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 FileTypeIdGlobalSettingsPanel.startUp.fileTypeDetectorInitializationException.msg=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u30c7\u30a3\u30c6\u30af\u30bf\u3092\u8d77\u52d5\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 FileTypeIdIngestModule.startUp.fileTypeDetectorInitializationException.msg=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u30c7\u30a3\u30c6\u30af\u30bf\u3092\u8d77\u52d5\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 -FileTypeIdGlobalSettingsPanel.offsetRelativeToLabel.text=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u6b21\u3068\u76f8\u5bfe\u7684 +AddFileTypeSignaturePanel.signatureTypeLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3\u30bf\u30a4\u30d7 +AddFileTypeSignaturePanel.signatureLabel.text=\u30b7\u30b0\u30cd\u30c1\u30e3 +AddFileTypeSignaturePanel.offsetRelativeToLabel.text=\u30aa\u30d5\u30bb\u30c3\u30c8\u306f\u6b21\u3068\u76f8\u5bfe\u7684 +AddFileTypeSignaturePanel.offsetLabel.text=\u30d0\u30a4\u30c8\u30aa\u30d5\u30bb\u30c3\u30c8 diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java index 432b4934e0..d645c1c4d5 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java @@ -19,16 +19,22 @@ package org.sleuthkit.autopsy.modules.filetypeid; import java.io.Serializable; +import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.logging.Level; +import javax.swing.JOptionPane; +import javax.xml.bind.DatatypeConverter; 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. + * Represents a file type characterized by file signatures. *

* Thread-safe (immutable). */ @@ -36,24 +42,24 @@ class FileType implements Serializable { private static final long serialVersionUID = 1L; private final String mimeType; - private final Signature signature; + private final List signatures; private final String interestingFilesSetName; private final boolean alert; /** - * Creates a representation of a file type characterized by a file - * signature. + * Creates a representation of a file type characterized by file + * signatures. * * @param mimeType The mime type to associate with this file type. - * @param signature The signature that characterizes this file type. + * @param signatures The signatures that characterize 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) { + FileType(String mimeType, List signatures, String filesSetName, boolean alert) { this.mimeType = mimeType; - this.signature = new Signature(signature.getSignatureBytes(), signature.getOffset(), signature.getType(), signature.isRelativeToStart()); + this.signatures = new ArrayList<>(signatures); this.interestingFilesSetName = filesSetName; this.alert = alert; } @@ -68,12 +74,16 @@ class FileType implements Serializable { } /** - * Gets the signature associated with this file type. + * Gets the signatures associated with this file type. * - * @return The signature. + * @return The signatures. */ - Signature getSignature() { - return new Signature(signature.getSignatureBytes(), signature.getOffset(), signature.getType(), signature.isRelativeToStart()); + List getSignatures() { + return Collections.unmodifiableList(this.signatures); + } + + void addSignature(Signature sig) { + this.signatures.add(sig); } /** @@ -84,7 +94,12 @@ class FileType implements Serializable { * @return True or false. */ boolean matches(final AbstractFile file) { - return signature.containedIn(file); + for (Signature sig : this.signatures) { + if (!sig.containedIn(file)) { + return false; + } + } + return true; } /** @@ -116,7 +131,7 @@ class FileType implements Serializable { public boolean equals(Object other) { if (other != null && other instanceof FileType) { FileType that = (FileType) other; - if (this.getMimeType().equals(that.getMimeType()) && this.getSignature().equals(that.getSignature())) { + if (this.getMimeType().equals(that.getMimeType()) && this.getSignatures().equals(that.getSignatures())) { return true; } } @@ -127,7 +142,7 @@ class FileType implements Serializable { public int hashCode() { int hash = 7; hash = 67 * hash + Objects.hashCode(this.mimeType); - hash = 67 * hash + Objects.hashCode(this.signature); + hash = 67 * hash + Objects.hashCode(this.signatures); return hash; } @@ -138,7 +153,7 @@ class FileType implements Serializable { * Thread-safe (immutable). */ static class Signature implements Serializable { - + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(Signature.class.getName()); @@ -191,8 +206,8 @@ class FileType implements Serializable { * string, use one of the other constructors so that the string is * displayed to the user instead of the raw bytes. * - * @param signatureBytes The signature bytes. - * @param offset The offset of the signature bytes. + * @param signatureBytes The signatures bytes. + * @param offset The offset of the signatures bytes. */ Signature(final byte[] signatureBytes, long offset) { this.signatureBytes = Arrays.copyOf(signatureBytes, signatureBytes.length); @@ -339,6 +354,33 @@ class FileType implements Serializable { hash = 97 * hash + Objects.hashCode(this.type); return hash; } + + @Override + public String toString() { + String signatureBytesString; + if (Signature.Type.RAW == this.getType()) { + signatureBytesString = DatatypeConverter.printHexBinary(this.getSignatureBytes()); + signatureBytesString = "0x" + signatureBytesString; + } else { + try { + signatureBytesString = new String(this.getSignatureBytes(), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + JOptionPane.showMessageDialog(null, + ex.getLocalizedMessage(), + Bundle.AddFileTypeSignaturePanel_signatureStringFail_text(), + JOptionPane.ERROR_MESSAGE); + signatureBytesString = ""; + } + } + String startOrEnd; + if (this.isRelativeToStart) { + startOrEnd = "start"; + } else { + startOrEnd = "end"; + } + return signatureBytesString + ", " + offset + " bytes from " + startOrEnd; + + } } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form index 7e8b342a16..0ac796f92c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.form @@ -3,10 +3,10 @@

- + - + @@ -33,71 +33,48 @@ - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - + @@ -115,7 +92,7 @@ - + @@ -134,29 +111,14 @@ - - - - - - - - - - - - + - - - + + + + - - - - - @@ -165,7 +127,6 @@ - @@ -232,57 +193,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -328,48 +238,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -448,32 +316,54 @@ - - - - - - - - - - - + - + - - + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java index ee5e9720bf..a2adf4e12a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java @@ -21,9 +21,9 @@ package org.sleuthkit.autopsy.modules.filetypeid; import java.awt.*; 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.List; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.JOptionPane; @@ -31,11 +31,12 @@ 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.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; +import org.sleuthkit.autopsy.modules.filetypeid.AddFileTypeSignatureDialog.BUTTON_PRESSED; import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; import org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException; @@ -59,6 +60,8 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane */ private DefaultListModel typesListModel; private java.util.List fileTypes; + private AddFileTypeSignatureDialog addSigDialog; + private DefaultListModel signaturesListModel; /** * This panel implements a property change listener that listens to ingest @@ -86,8 +89,9 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane */ @NbBundle.Messages({"FileTypeIdGlobalSettingsPanel.Title=Global File Type Identification Settings"}) private void customizeComponents() { - setName(Bundle.FileTypeIdGlobalSettingsPanel_Title()); + setName(Bundle.FileTypeIdGlobalSettingsPanel_Title()); setFileTypesListModel(); + setSignaturesListModel(); setSignatureTypeComboBoxModel(); setOffsetRealtiveToComboBoxModel(); clearTypeDetailsComponents(); @@ -103,6 +107,11 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane typesList.setModel(typesListModel); } + private void setSignaturesListModel() { + this.signaturesListModel = new DefaultListModel<>(); + signatureList.setModel(signaturesListModel); + } + /** * Sets the model for the signature type combo box. */ @@ -110,10 +119,8 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane DefaultComboBoxModel 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); } - + /** * Sets the model for the signature type combo box. */ @@ -121,8 +128,6 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane DefaultComboBoxModel offsetRelComboBoxModel = new DefaultComboBoxModel<>(); offsetRelComboBoxModel.addElement(FileTypeIdGlobalSettingsPanel.START_OFFSET_RELATIVE_COMBO_BOX_ITEM); offsetRelComboBoxModel.addElement(FileTypeIdGlobalSettingsPanel.END_OFFSET_RELATIVE_COMBO_BOX_ITEM); - offsetRelativeToComboBox.setModel(offsetRelComboBoxModel); - offsetRelativeToComboBox.setSelectedItem(FileTypeIdGlobalSettingsPanel.START_OFFSET_RELATIVE_COMBO_BOX_ITEM); } /** @@ -168,8 +173,6 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane }; mimeTypeTextField.getDocument().addDocumentListener(listener); - offsetTextField.getDocument().addDocumentListener(listener); - signatureTextField.getDocument().addDocumentListener(listener); filesSetNameTextField.getDocument().addDocumentListener(listener); } @@ -216,8 +219,6 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane boolean requiredFieldsPopulated = !mimeTypeTextField.getText().isEmpty() - && !offsetTextField.getText().isEmpty() - && !signatureTextField.getText().isEmpty() && (postHitCheckBox.isSelected() ? !filesSetNameTextField.getText().isEmpty() : true); saveTypeButton.setEnabled(!ingestIsRunning && requiredFieldsPopulated); @@ -265,29 +266,18 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane if (null != fileType) { mimeTypeTextField.setText(fileType.getMimeType()); mimeTypeTextField.setEditable(false); - 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 = ""; - } + List signatures = fileType.getSignatures(); + this.signaturesListModel.clear(); + for (Signature sig : signatures) { + signaturesListModel.addElement(sig); } - signatureTextField.setText(signatureBytes); - offsetRelativeToComboBox.setSelectedItem(signature.isRelativeToStart() ? FileTypeIdGlobalSettingsPanel.START_OFFSET_RELATIVE_COMBO_BOX_ITEM : FileTypeIdGlobalSettingsPanel.END_OFFSET_RELATIVE_COMBO_BOX_ITEM); - offsetTextField.setText(Long.toString(signature.getOffset())); postHitCheckBox.setSelected(fileType.alertOnMatch()); filesSetNameTextField.setEnabled(postHitCheckBox.isSelected()); filesSetNameTextField.setText(fileType.getFilesSetName()); + this.signatureList.setEnabled(false); + this.addSigButton.setEnabled(false); + this.deleteSigButton.setEnabled(false); + this.editSigButton.setEnabled(false); } enableButtons(); } @@ -300,14 +290,14 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane typesList.clearSelection(); mimeTypeTextField.setText(""); //NON-NLS mimeTypeTextField.setEditable(true); - signatureTypeComboBox.setSelectedItem(FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM); - hexPrefixLabel.setVisible(true); - signatureTextField.setText("0000"); //NON-NLS - offsetRelativeToComboBox.setSelectedItem(FileTypeIdGlobalSettingsPanel.START_OFFSET_RELATIVE_COMBO_BOX_ITEM); - offsetTextField.setText(""); //NON-NLS + this.signatureList.setEnabled(true); + this.addSigButton.setEnabled(true); + this.deleteSigButton.setEnabled(true); + this.editSigButton.setEnabled(true); postHitCheckBox.setSelected(false); filesSetNameTextField.setText(""); //NON-NLS filesSetNameTextField.setEnabled(false); + this.signaturesListModel.clear(); enableButtons(); } @@ -354,31 +344,27 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane private void initComponents() { typesScrollPane = new javax.swing.JScrollPane(); - typesList = new javax.swing.JList(); + typesList = new javax.swing.JList<>(); 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(); - signatureLabel = new javax.swing.JLabel(); postHitCheckBox = new javax.swing.JCheckBox(); filesSetNameLabel = new javax.swing.JLabel(); filesSetNameTextField = new javax.swing.JTextField(); ingestRunningWarningLabel = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel(); - offsetRelativeToComboBox = new javax.swing.JComboBox(); - offsetRelativeToLabel = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); + signatureList = new javax.swing.JList<>(); + addSigButton = new javax.swing.JButton(); + editSigButton = new javax.swing.JButton(); + deleteSigButton = new javax.swing.JButton(); - setMaximumSize(new java.awt.Dimension(500, 300)); - setPreferredSize(new java.awt.Dimension(500, 300)); + setMaximumSize(new java.awt.Dimension(552, 297)); + setPreferredSize(new java.awt.Dimension(552, 297)); typesList.setFont(typesList.getFont().deriveFont(typesList.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); typesList.setMaximumSize(new java.awt.Dimension(150, 0)); @@ -393,23 +379,6 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane mimeTypeTextField.setFont(mimeTypeTextField.getFont().deriveFont(mimeTypeTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); mimeTypeTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.mimeTypeTextField.text")); // NOI18N - signatureTypeLabel.setFont(signatureTypeLabel.getFont().deriveFont(signatureTypeLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - org.openide.awt.Mnemonics.setLocalizedText(signatureTypeLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureTypeLabel.text")); // NOI18N - - signatureTextField.setFont(signatureTextField.getFont().deriveFont(signatureTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - signatureTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureTextField.text")); // NOI18N - signatureTextField.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - signatureTextFieldActionPerformed(evt); - } - }); - - offsetLabel.setFont(offsetLabel.getFont().deriveFont(offsetLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - org.openide.awt.Mnemonics.setLocalizedText(offsetLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetLabel.text")); // NOI18N - - offsetTextField.setFont(offsetTextField.getFont().deriveFont(offsetTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - offsetTextField.setText(org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetTextField.text")); // NOI18N - newTypeButton.setFont(newTypeButton.getFont().deriveFont(newTypeButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); org.openide.awt.Mnemonics.setLocalizedText(newTypeButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.newTypeButton.text")); // NOI18N newTypeButton.addActionListener(new java.awt.event.ActionListener() { @@ -434,19 +403,6 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane } }); - hexPrefixLabel.setFont(hexPrefixLabel.getFont().deriveFont(hexPrefixLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - org.openide.awt.Mnemonics.setLocalizedText(hexPrefixLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.hexPrefixLabel.text")); // NOI18N - - signatureTypeComboBox.setFont(signatureTypeComboBox.getFont().deriveFont(signatureTypeComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - signatureTypeComboBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - signatureTypeComboBoxActionPerformed(evt); - } - }); - - signatureLabel.setFont(signatureLabel.getFont().deriveFont(signatureLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - org.openide.awt.Mnemonics.setLocalizedText(signatureLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureLabel.text")); // NOI18N - postHitCheckBox.setFont(postHitCheckBox.getFont().deriveFont(postHitCheckBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); org.openide.awt.Mnemonics.setLocalizedText(postHitCheckBox, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.postHitCheckBox.text")); // NOI18N postHitCheckBox.addActionListener(new java.awt.event.ActionListener() { @@ -471,10 +427,33 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane jLabel3.setFont(jLabel3.getFont().deriveFont(jLabel3.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.jLabel3.text")); // NOI18N - offsetRelativeToComboBox.setFont(offsetRelativeToComboBox.getFont().deriveFont(offsetRelativeToComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + signatureList.setModel(new javax.swing.AbstractListModel() { + Signature[] signatures = {}; + public int getSize() { return signatures.length; } + public Signature getElementAt(int i) { return signatures[i]; } + }); + jScrollPane1.setViewportView(signatureList); - offsetRelativeToLabel.setFont(offsetRelativeToLabel.getFont().deriveFont(offsetRelativeToLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - org.openide.awt.Mnemonics.setLocalizedText(offsetRelativeToLabel, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.offsetRelativeToLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(addSigButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.addSigButton.text")); // NOI18N + addSigButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addSigButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(editSigButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.editSigButton.text")); // NOI18N + editSigButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + editSigButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(deleteSigButton, org.openide.util.NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.deleteSigButton.text")); // NOI18N + deleteSigButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteSigButtonActionPerformed(evt); + } + }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -488,56 +467,39 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane .addGap(30, 30, 30)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel2) - .addGroup(layout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(deleteTypeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(newTypeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(typesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 7, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel3) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(saveTypeButton) + .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(21, 21, 21) + .addComponent(jLabel2) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(deleteTypeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(newTypeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(typesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 7, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() .addComponent(filesSetNameLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(filesSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 182, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(filesSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 277, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 333, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(postHitCheckBox) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(mimeTypeLabel) - .addGap(30, 30, 30) - .addComponent(mimeTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(postHitCheckBox) - .addGroup(layout.createSequentialGroup() - .addComponent(signatureTypeLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addComponent(signatureLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(hexPrefixLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(signatureTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)) - .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))) - .addGap(6, 6, 6))) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(saveTypeButton) - .addGap(8, 8, 8)) - .addGroup(layout.createSequentialGroup() - .addComponent(offsetRelativeToLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(offsetRelativeToComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addComponent(jLabel3)) - .addContainerGap(28, Short.MAX_VALUE)))) + .addComponent(addSigButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(editSigButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(deleteSigButton)) + .addGroup(layout.createSequentialGroup() + .addComponent(mimeTypeLabel) + .addGap(18, 18, 18) + .addComponent(mimeTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 262, javax.swing.GroupLayout.PREFERRED_SIZE)))))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -551,7 +513,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane .addGroup(layout.createSequentialGroup() .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(typesScrollPane) + .addComponent(typesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 183, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(deleteTypeButton) @@ -565,32 +527,20 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane .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.LEADING) - .addComponent(signatureTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(signatureTypeLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(hexPrefixLabel) - .addComponent(signatureTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(signatureLabel)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(offsetLabel) - .addComponent(offsetTextField, 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(offsetRelativeToComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(offsetRelativeToLabel)) - .addGap(16, 16, 16) + .addComponent(addSigButton) + .addComponent(editSigButton) + .addComponent(deleteSigButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(postHitCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .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)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(saveTypeButton) - .addGap(0, 21, Short.MAX_VALUE)))) + .addComponent(saveTypeButton)))) ); layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteTypeButton, newTypeButton, saveTypeButton}); @@ -609,11 +559,10 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane typesList.setSelectedIndex(0); } }//GEN-LAST:event_deleteTypeButtonActionPerformed - + @Messages({ + "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSigList.message=Must have at least one signature.", + "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSigList.title=Invalid Signature List"}) 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, @@ -622,61 +571,17 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane 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()) { + List sigList = new ArrayList<>(); + for (int i = 0; i < this.signaturesListModel.getSize(); i++) { + sigList.add(this.signaturesListModel.elementAt(i)); + } + if (sigList.isEmpty()) { JOptionPane.showMessageDialog(null, - NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.message"), - NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignature.title"), + Bundle.AddFileTypeSignatureDialog_invalidSignature_message(), + Bundle.FileTypeIdGlobalSettingsPanel_JOptionPane_invalidSigList_title(), JOptionPane.ERROR_MESSAGE); return; } - byte[] signatureBytes; - if (FileType.Signature.Type.RAW == sigType) { - try { - sigString = sigString.replaceAll("\\s", ""); //NON-NLS - 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; - boolean isRelativeToStart = offsetRelativeToComboBox.getSelectedItem() == FileTypeIdGlobalSettingsPanel.START_OFFSET_RELATIVE_COMBO_BOX_ITEM; - try { - offset = Long.parseUnsignedLong(offsetTextField.getText()); - if(!isRelativeToStart && signatureBytes.length > offset+1) { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.length"), - NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - } 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. */ @@ -691,12 +596,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane JOptionPane.ERROR_MESSAGE); return; } - - /** - * Put it all together and reset the file types list component. - */ - FileType.Signature signature = new FileType.Signature(signatureBytes, offset, sigType, isRelativeToStart); - FileType fileType = new FileType(typeName, signature, filesSetName, postHitCheckBox.isSelected()); + FileType fileType = new FileType(typeName, sigList, filesSetName, postHitCheckBox.isSelected()); FileType selected = typesList.getSelectedValue(); if (selected != null) { fileTypes.remove(selected); @@ -711,42 +611,52 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane enableButtons(); }//GEN-LAST:event_postHitCheckBoxActionPerformed - private void signatureTypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_signatureTypeComboBoxActionPerformed - if (signatureTypeComboBox.getSelectedItem() == FileTypeIdGlobalSettingsPanel.RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM) { - hexPrefixLabel.setVisible(true); - signatureTextField.setText("0000"); - } else { - hexPrefixLabel.setVisible(false); - signatureTextField.setText(""); + private void addSigButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addSigButtonActionPerformed + if (evt.getSource().equals(this.addSigButton)) { + this.addSigDialog = new AddFileTypeSignatureDialog(); + if (addSigDialog.getResult() == BUTTON_PRESSED.OK) { + signaturesListModel.addElement(this.addSigDialog.getSignature()); + } } - }//GEN-LAST:event_signatureTypeComboBoxActionPerformed + }//GEN-LAST:event_addSigButtonActionPerformed - private void signatureTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_signatureTextFieldActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_signatureTextFieldActionPerformed + private void deleteSigButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteSigButtonActionPerformed + if (this.signatureList.getSelectedIndex() != -1) { + signaturesListModel.removeElementAt(this.signatureList.getSelectedIndex()); + if (!this.signaturesListModel.isEmpty()) { + signatureList.setSelectedIndex(0); + } + } + }//GEN-LAST:event_deleteSigButtonActionPerformed + + private void editSigButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editSigButtonActionPerformed + if (evt.getSource().equals(this.editSigButton) && this.signatureList.getSelectedValue() != null) { + this.addSigDialog = new AddFileTypeSignatureDialog(this.signatureList.getSelectedValue()); + if (addSigDialog.getResult() == BUTTON_PRESSED.OK) { + signaturesListModel.removeElementAt(this.signatureList.getSelectedIndex()); + this.signaturesListModel.addElement(this.addSigDialog.getSignature()); + } + } + }//GEN-LAST:event_editSigButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addSigButton; + private javax.swing.JButton deleteSigButton; private javax.swing.JButton deleteTypeButton; + private javax.swing.JButton editSigButton; private javax.swing.JLabel filesSetNameLabel; private javax.swing.JTextField filesSetNameTextField; - private javax.swing.JLabel hexPrefixLabel; private javax.swing.JLabel ingestRunningWarningLabel; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel mimeTypeLabel; private javax.swing.JTextField mimeTypeTextField; private javax.swing.JButton newTypeButton; - private javax.swing.JLabel offsetLabel; - private javax.swing.JComboBox offsetRelativeToComboBox; - private javax.swing.JLabel offsetRelativeToLabel; - 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 signatureTypeComboBox; - private javax.swing.JLabel signatureTypeLabel; + private javax.swing.JList signatureList; 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/UserDefinedFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java index 6ae8404c13..ff5ed5a8f9 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java @@ -29,12 +29,8 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; -import javax.persistence.PersistenceException; -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.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.openide.util.NbBundle; import org.openide.util.io.NbObjectInputStream; @@ -43,8 +39,10 @@ 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.datamodel.TskCoreException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** @@ -188,56 +186,123 @@ final class UserDefinedFileTypesManager { FileType fileType; try { - // Add rule for xml - fileType = new FileType("text/xml", new Signature(" signatureList; + signatureList = new ArrayList<>(); + signatureList.add(new Signature(" sigList = new ArrayList<>(); + sigList.add(signature); + return new FileType(mimeType, sigList, filesSetName, alert); } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java index ada9d6f899..d12be8d8a6 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 - 2014 Basis Technology Corp. + * Copyright 2011 - 2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,43 +19,36 @@ package org.sleuthkit.autopsy.modules.hashdatabase; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; -import org.sleuthkit.autopsy.coreutils.XMLUtil; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.SwingWorker; +import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.FileUtils; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupSettings.HashDbInfo; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.HashEntry; +import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; - -import org.sleuthkit.autopsy.ingest.IngestManager; /** * This class implements a singleton that manages the set of hash databases used @@ -63,20 +56,8 @@ import org.sleuthkit.autopsy.ingest.IngestManager; */ public class HashDbManager implements PropertyChangeListener { - private static final String ROOT_ELEMENT = "hash_sets"; //NON-NLS - private static final String SET_ELEMENT = "hash_set"; //NON-NLS - private static final String SET_NAME_ATTRIBUTE = "name"; //NON-NLS - private static final String SET_TYPE_ATTRIBUTE = "type"; //NON-NLS - private static final String SEARCH_DURING_INGEST_ATTRIBUTE = "use_for_ingest"; //NON-NLS - private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = "show_inbox_messages"; //NON-NLS - private static final String PATH_ELEMENT = "hash_set_path"; //NON-NLS - private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = "number"; //NON-NLS - private static final String CONFIG_FILE_NAME = "hashsets.xml"; //NON-NLS - private static final String XSD_FILE_NAME = "HashsetsSchema.xsd"; //NON-NLS - private static final String ENCODING = "UTF-8"; //NON-NLS private static final String HASH_DATABASE_FILE_EXTENSON = "kdb"; //NON-NLS private static HashDbManager instance = null; - private final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME; private List knownHashSets = new ArrayList<>(); private List knownBadHashSets = new ArrayList<>(); private Set hashSetNames = new HashSet<>(); @@ -108,10 +89,12 @@ public class HashDbManager implements PropertyChangeListener { changeSupport.addPropertyChangeListener(listener); } + public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + private HashDbManager() { - if (hashSetsConfigurationFileExists()) { - readHashSetsConfigurationFromDisk(); - } + loadHashsetsConfiguration(); } /** @@ -125,9 +108,15 @@ public class HashDbManager implements PropertyChangeListener { public class HashDbManagerException extends Exception { + private static final long serialVersionUID = 1L; + private HashDbManagerException(String message) { super(message); } + + private HashDbManagerException(String message, Throwable exception) { + super(message, exception); + } } /** @@ -477,7 +466,11 @@ public class HashDbManager implements PropertyChangeListener { * @return True on success, false otherwise. */ synchronized boolean save() { - return writeHashSetConfigurationToDisk(); + try { + return HashLookupSettings.writeSettings(new HashLookupSettings(this.knownHashSets, this.knownBadHashSets)); + } catch (HashLookupSettings.HashLookupSettingsException ex) { + return false; + } } /** @@ -490,9 +483,7 @@ public class HashDbManager implements PropertyChangeListener { hashSetNames.clear(); hashSetPaths.clear(); - if (hashSetsConfigurationFileExists()) { - readHashSetsConfigurationFromDisk(); - } + loadHashsetsConfiguration(); } private void closeHashDatabases(List hashDatabases) { @@ -506,198 +497,51 @@ public class HashDbManager implements PropertyChangeListener { hashDatabases.clear(); } - private boolean writeHashSetConfigurationToDisk() { - boolean success = false; - DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); + private void loadHashsetsConfiguration() { try { - DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); - Document doc = docBuilder.newDocument(); - Element rootEl = doc.createElement(ROOT_ELEMENT); - doc.appendChild(rootEl); - - writeHashDbsToDisk(doc, rootEl, knownHashSets); - writeHashDbsToDisk(doc, rootEl, knownBadHashSets); - - success = XMLUtil.saveDoc(HashDbManager.class, configFilePath, ENCODING, doc); - } catch (ParserConfigurationException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error saving hash databases", ex); //NON-NLS + HashLookupSettings settings = HashLookupSettings.readSettings(); + this.configureSettings(settings); + } catch (HashLookupSettings.HashLookupSettingsException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Could not read Hash lookup settings from disk.", ex); } - return success; } - private static void writeHashDbsToDisk(Document doc, Element rootEl, List hashDbs) { - for (HashDb db : hashDbs) { - // Get the path for the hash database before writing anything, in - // case an exception is thrown. - String path; + /** + * Configures the given settings object by adding all contained hash db to + * the system. + * + * @param settings The settings to configure. + */ + @Messages({"# {0} - database name", "HashDbManager.noDbPath.message=Couldn't get valid database path for: {0}"}) + private void configureSettings(HashLookupSettings settings) { + boolean dbInfoRemoved = false; + List hashDbInfoList = settings.getHashDbInfo(); + for (HashDbInfo hashDb : hashDbInfoList) { try { - if (db.hasIndexOnly()) { - path = db.getIndexPath(); + String dbPath = this.getValidFilePath(hashDb.getHashSetName(), hashDb.getPath()); + if (dbPath != null) { + addExistingHashDatabaseInternal(hashDb.getHashSetName(), getValidFilePath(hashDb.getHashSetName(), hashDb.getPath()), hashDb.getSearchDuringIngest(), hashDb.getSendIngestMessages(), hashDb.getKnownFilesType()); } else { - path = db.getDatabasePath(); + logger.log(Level.WARNING, Bundle.HashDbManager_noDbPath_message(hashDb.getHashSetName())); + dbInfoRemoved = true; } - } catch (TskCoreException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting path of hash database " + db.getHashSetName() + ", discarding from hash database configuration", ex); //NON-NLS - continue; - } - - Element setElement = doc.createElement(SET_ELEMENT); - setElement.setAttribute(SET_NAME_ATTRIBUTE, db.getHashSetName()); - setElement.setAttribute(SET_TYPE_ATTRIBUTE, db.getKnownFilesType().toString()); - setElement.setAttribute(SEARCH_DURING_INGEST_ATTRIBUTE, Boolean.toString(db.getSearchDuringIngest())); - setElement.setAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE, Boolean.toString(db.getSendIngestMessages())); - Element pathElement = doc.createElement(PATH_ELEMENT); - pathElement.setTextContent(path); - setElement.appendChild(pathElement); - rootEl.appendChild(setElement); - } - } - - private boolean hashSetsConfigurationFileExists() { - File f = new File(configFilePath); - return f.exists() && f.canRead() && f.canWrite(); - } - - private boolean readHashSetsConfigurationFromDisk() { - boolean updatedSchema = false; - - // Open the XML document that implements the configuration file. - final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath); - if (doc == null) { - return false; - } - - // Get the root element. - Element root = doc.getDocumentElement(); - if (root == null) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading hash sets: invalid file format."); //NON-NLS - return false; - } - - // Get the hash set elements. - NodeList setsNList = root.getElementsByTagName(SET_ELEMENT); - int numSets = setsNList.getLength(); - if (numSets == 0) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No element hash_set exists."); //NON-NLS - } - - // Create HashDb objects for each hash set element. Skip to the next hash database if the definition of - // a particular hash database is not well-formed. - String attributeErrorMessage = " attribute was not set for hash_set at index {0}, cannot make instance of HashDb class"; //NON-NLS - String elementErrorMessage = " element was not set for hash_set at index {0}, cannot make instance of HashDb class"; //NON-NLS - for (int i = 0; i < numSets; ++i) { - Element setEl = (Element) setsNList.item(i); - - String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE); - if (hashSetName.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_NAME_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - - // Handle configurations saved before duplicate hash set names were not permitted. - if (hashSetNames.contains(hashSetName)) { - int suffix = 0; - String newHashSetName; - do { - ++suffix; - newHashSetName = hashSetName + suffix; - } while (hashSetNames.contains(newHashSetName)); + } catch (HashDbManagerException | TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); //NON-NLS JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(), - "HashDbManager.replacingDuplicateHashsetNameMsg", - hashSetName, newHashSetName), + "HashDbManager.unableToOpenHashDbMsg", hashDb.getHashSetName()), NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), JOptionPane.ERROR_MESSAGE); - hashSetName = newHashSetName; - } - - String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE); - if (knownFilesType.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_TYPE_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - - // Handle legacy known files types. - if (knownFilesType.equals("NSRL")) { //NON-NLS - knownFilesType = HashDb.KnownFilesType.KNOWN.toString(); - updatedSchema = true; - } - - final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE); - if (searchDuringIngest.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEARCH_DURING_INGEST_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - Boolean seearchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest); - - final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE); - if (searchDuringIngest.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEND_INGEST_MESSAGES_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages); - - String dbPath; - NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT); - if (pathsNList.getLength() > 0) { - Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one. - - // Check for legacy path number attribute. - String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE); - if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) { - updatedSchema = true; - } - - dbPath = pathEl.getTextContent(); - if (dbPath.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); - continue; - } - } else { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); - continue; - } - dbPath = getValidFilePath(hashSetName, dbPath); - - if (null != dbPath) { - try { - addExistingHashDatabaseInternal(hashSetName, dbPath, seearchDuringIngestFlag, sendIngestMessagesFlag, HashDb.KnownFilesType.valueOf(knownFilesType)); - } catch (HashDbManagerException | TskCoreException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); //NON-NLS - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "HashDbManager.unableToOpenHashDbMsg", dbPath), - NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), - JOptionPane.ERROR_MESSAGE); - } - } else { - Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); //NON-NLS + dbInfoRemoved = true; } } - - if (updatedSchema) { - String backupFilePath = configFilePath + ".v1_backup"; //NON-NLS - String messageBoxTitle = NbBundle.getMessage(this.getClass(), - "HashDbManager.msgBoxTitle.confFileFmtChanged"); - String baseMessage = NbBundle.getMessage(this.getClass(), - "HashDbManager.baseMessage.updatedFormatHashDbConfig"); + if (dbInfoRemoved) { try { - FileUtils.copyFile(new File(configFilePath), new File(backupFilePath)); - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "HashDbManager.savedBackupOfOldConfigMsg", - baseMessage, backupFilePath), - messageBoxTitle, - JOptionPane.INFORMATION_MESSAGE); - } catch (IOException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "Failed to save backup of old format configuration file to " + backupFilePath, ex); //NON-NLS - JOptionPane.showMessageDialog(null, baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE); + HashLookupSettings.writeSettings(new HashLookupSettings(this.knownHashSets, this.knownBadHashSets)); + } catch (HashLookupSettings.HashLookupSettingsException ex) { + logger.log(Level.SEVERE, "Could not overwrite hash database settings.", ex); } - - writeHashSetConfigurationToDisk(); } - - return true; } private String getValidFilePath(String hashSetName, String configuredPath) { @@ -760,7 +604,7 @@ public class HashDbManager implements PropertyChangeListener { KNOWN(NbBundle.getMessage(HashDbManager.class, "HashDbManager.known.text")), KNOWN_BAD(NbBundle.getMessage(HashDbManager.class, "HashDbManager.knownBad.text")); - private String displayName; + private final String displayName; private KnownFilesType(String displayName) { this.displayName = displayName; @@ -778,11 +622,12 @@ public class HashDbManager implements PropertyChangeListener { INDEXING_DONE } - private int handle; - private String hashSetName; + private static final long serialVersionUID = 1L; + private final int handle; + private final String hashSetName; private boolean searchDuringIngest; private boolean sendIngestMessages; - private KnownFilesType knownFilesType; + private final KnownFilesType knownFilesType; private boolean indexing; private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); @@ -797,6 +642,8 @@ public class HashDbManager implements PropertyChangeListener { /** * Adds a listener for the events defined in HashDb.Event. + * + * @param pcl */ public void addPropertyChangeListener(PropertyChangeListener pcl) { propertyChangeSupport.addPropertyChangeListener(pcl); @@ -804,6 +651,8 @@ public class HashDbManager implements PropertyChangeListener { /** * Removes a listener for the events defined in HashDb.Event. + * + * @param pcl */ public void removePropertyChangeListener(PropertyChangeListener pcl) { propertyChangeSupport.removePropertyChangeListener(pcl); @@ -845,6 +694,8 @@ public class HashDbManager implements PropertyChangeListener { * Indicates whether the hash database accepts updates. * * @return True if the database accepts updates, false otherwise. + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public boolean isUpdateable() throws TskCoreException { return SleuthkitJNI.isUpdateableHashDatabase(this.handle); @@ -956,6 +807,40 @@ public class HashDbManager implements PropertyChangeListener { private void close() throws TskCoreException { SleuthkitJNI.closeHashDatabase(handle); } + + @Override + public int hashCode() { + int code = 23; + code = 47 * code + Integer.hashCode(handle); + code = 47 * code + Objects.hashCode(this.hashSetName); + code = 47 * code + Objects.hashCode(this.propertyChangeSupport); + code = 47 * code + Objects.hashCode(this.knownFilesType); + return code; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final HashDb other = (HashDb) obj; + if (!Objects.equals(this.hashSetName, other.hashSetName)) { + return false; + } + if (this.searchDuringIngest != other.searchDuringIngest) { + return false; + } + if (this.sendIngestMessages != other.sendIngestMessages) { + return false; + } + if (this.knownFilesType != other.knownFilesType) { + return false; + } + return true; + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java new file mode 100755 index 0000000000..ae52f86219 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java @@ -0,0 +1,406 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 - 2016 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.hashdatabase; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import javax.swing.JOptionPane; +import org.apache.commons.io.FileUtils; +import org.openide.util.NbBundle; +import org.openide.util.io.NbObjectInputStream; +import org.openide.util.io.NbObjectOutputStream; +import org.sleuthkit.autopsy.core.RuntimeProperties; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.coreutils.XMLUtil; +import org.sleuthkit.datamodel.TskCoreException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * Class to represent the settings to be serialized for hash lookup. + */ +final class HashLookupSettings implements Serializable { + + private static final String SERIALIZATION_FILE_NAME = "hashLookup.settings"; //NON-NLS + private static final String SERIALIZATION_FILE_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + SERIALIZATION_FILE_NAME; //NON-NLS + private static final String SET_ELEMENT = "hash_set"; //NON-NLS + private static final String SET_NAME_ATTRIBUTE = "name"; //NON-NLS + private static final String SET_TYPE_ATTRIBUTE = "type"; //NON-NLS + private static final String SEARCH_DURING_INGEST_ATTRIBUTE = "use_for_ingest"; //NON-NLS + private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = "show_inbox_messages"; //NON-NLS + private static final String PATH_ELEMENT = "hash_set_path"; //NON-NLS + private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = "number"; //NON-NLS + private static final String CONFIG_FILE_NAME = "hashsets.xml"; //NON-NLS + private static final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME; + private static final Logger logger = Logger.getLogger(HashDbManager.class.getName()); + + private static final long serialVersionUID = 1L; + private final List hashDbInfoList; + + /** + * Constructs a settings object to be serialized for hash lookups + * + * @param hashDbInfoList The list of hash db info. + */ + HashLookupSettings(List hashDbInfoList) { + this.hashDbInfoList = hashDbInfoList; + } + + /** + * Constructs a settings object to be serialized for hash lookups + * + * @param knownHashSets The list known hash sets for the settings. + * @param knownBadHashSets The list of known bad hash sets for the settings. + */ + HashLookupSettings(List knownHashSets, List knownBadHashSets) throws HashLookupSettingsException { + hashDbInfoList = new ArrayList<>(); + this.addHashesToList(knownHashSets); + this.addHashesToList(knownBadHashSets); + } + + /** + * Adds each HashDb to the settings. + * + * @param hashSetList The list of HashDb to add to the settings + * + * @throws * pacannot be obtained + */ + private void addHashesToList(List hashSetList) throws HashLookupSettingsException { + for (HashDbManager.HashDb hashDb : hashSetList) { + try { + String dbPath; + if (hashDb.hasIndexOnly()) { + dbPath = hashDb.getIndexPath(); + } else { + dbPath = hashDb.getDatabasePath(); + } + hashDbInfoList.add(new HashDbInfo(hashDb.getHashSetName(), hashDb.getKnownFilesType(), hashDb.getSearchDuringIngest(), hashDb.getSendIngestMessages(), dbPath)); + } catch (TskCoreException ex) { + throw new HashLookupSettingsException("Couldn't add hash database named: " + hashDb.getHashSetName(), ex); + } + } + } + + /** + * Gets the list of hash db info that this settings contains + * + * @return The list of hash databse info + */ + List getHashDbInfo() { + return hashDbInfoList; + } + + /** + * Reads the settings from the disk. + * + * @return The settings object representing what was read. + * + * @throws HashLookupSettingsException When there is a problem reading the + * settings. + */ + static HashLookupSettings readSettings() throws HashLookupSettingsException { + File fileSetFile = new File(SERIALIZATION_FILE_PATH); + if (fileSetFile.exists()) { + return readSerializedSettings(); + } + return readXmlSettings(); + + } + + /** + * Reads the serialization settings from the disk + * + * @return Settings object representing what is saved in the serialization + * file. + * + * @throws HashLookupSettingsException If there's a problem importing the + * settings + */ + private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException { + try { + try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(SERIALIZATION_FILE_PATH))) { + HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject(); + return filesSetsSettings; + } + } catch (IOException | ClassNotFoundException ex) { + throw new HashLookupSettingsException("Could not read hash database settings.", ex); + } + } + + /** + * Reads the xml settings from the disk + * + * @return Settings object representing what is saved in the xml file, or an + * empty settings if there is no xml file. + * + * @throws HashLookupSettingsException If there's a problem importing the + * settings + */ + private static HashLookupSettings readXmlSettings() throws HashLookupSettingsException { + File xmlFile = new File(configFilePath); + if (xmlFile.exists()) { + boolean updatedSchema = false; + + // Open the XML document that implements the configuration file. + final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath); + if (doc == null) { + throw new HashLookupSettingsException("Could not open xml document."); + } + + // Get the root element. + Element root = doc.getDocumentElement(); + if (root == null) { + throw new HashLookupSettingsException("Error loading hash sets: invalid file format."); + } + + // Get the hash set elements. + NodeList setsNList = root.getElementsByTagName(SET_ELEMENT); + int numSets = setsNList.getLength(); + + // Create HashDbInfo objects for each hash set element. Throws on malformed xml. + String attributeErrorMessage = "Missing %s attribute"; //NON-NLS + String elementErrorMessage = "Empty %s element"; //NON-NLS + List hashSetNames = new ArrayList<>(); + List hashDbInfoList = new ArrayList<>(); + for (int i = 0; i < numSets; ++i) { + Element setEl = (Element) setsNList.item(i); + + String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE); + if (hashSetName.isEmpty()) { + throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_NAME_ATTRIBUTE)); + } + + // Handle configurations saved before duplicate hash set names were not permitted. + if (hashSetNames.contains(hashSetName)) { + int suffix = 0; + String newHashSetName; + do { + ++suffix; + newHashSetName = hashSetName + suffix; + } while (hashSetNames.contains(newHashSetName)); + logger.log(Level.INFO, "Duplicate hash set name " + hashSetName + " found. Replacing with " + newHashSetName + "."); + if (RuntimeProperties.coreComponentsAreActive()) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(HashLookupSettings.class, + "HashDbManager.replacingDuplicateHashsetNameMsg", + hashSetName, newHashSetName), + NbBundle.getMessage(HashLookupSettings.class, "HashDbManager.openHashDbErr"), + JOptionPane.ERROR_MESSAGE); + hashSetName = newHashSetName; + } + } + + String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE); + if (knownFilesType.isEmpty()) { + throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_TYPE_ATTRIBUTE)); + } + + // Handle legacy known files types. + if (knownFilesType.equals("NSRL")) { //NON-NLS + knownFilesType = HashDbManager.HashDb.KnownFilesType.KNOWN.toString(); + updatedSchema = true; + } + + final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE); + if (searchDuringIngest.isEmpty()) { + throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE)); + } + Boolean searchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest); + + final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE); + if (searchDuringIngest.isEmpty()) { + throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE)); + } + Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages); + + String dbPath; + NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT); + if (pathsNList.getLength() > 0) { + Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one. + + // Check for legacy path number attribute. + String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE); + if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) { + updatedSchema = true; + } + + dbPath = pathEl.getTextContent(); + if (dbPath.isEmpty()) { + throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT)); + } + } else { + throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT)); + } + hashDbInfoList.add(new HashDbInfo(hashSetName, HashDbManager.HashDb.KnownFilesType.valueOf(knownFilesType), + searchDuringIngestFlag, sendIngestMessagesFlag, dbPath)); + hashSetNames.add(hashSetName); + } + + if (updatedSchema) { + String backupFilePath = configFilePath + ".v1_backup"; //NON-NLS + String messageBoxTitle = NbBundle.getMessage(HashLookupSettings.class, + "HashDbManager.msgBoxTitle.confFileFmtChanged"); + String baseMessage = NbBundle.getMessage(HashLookupSettings.class, + "HashDbManager.baseMessage.updatedFormatHashDbConfig"); + try { + FileUtils.copyFile(new File(configFilePath), new File(backupFilePath)); + logger.log(Level.INFO, "Updated the schema, backup saved at: " + backupFilePath); + if (RuntimeProperties.coreComponentsAreActive()) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(HashLookupSettings.class, + "HashDbManager.savedBackupOfOldConfigMsg", + baseMessage, backupFilePath), + messageBoxTitle, + JOptionPane.INFORMATION_MESSAGE); + } + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to save backup of old format configuration file to " + backupFilePath, ex); //NON-NLS + JOptionPane.showMessageDialog(null, baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE); + } + HashLookupSettings settings; + settings = new HashLookupSettings(hashDbInfoList); + HashLookupSettings.writeSettings(settings); + } + return new HashLookupSettings(hashDbInfoList); + } else { + return new HashLookupSettings(new ArrayList<>()); + } + } + + /** + * Writes the given settings objects to the disk at the designated location + * + * @param settings The settings to be written + * + * @return Whether or not the settings were written successfully + */ + static boolean writeSettings(HashLookupSettings settings) { + try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(SERIALIZATION_FILE_PATH))) { + out.writeObject(settings); + return true; + } catch (Exception ex) { + logger.log(Level.SEVERE, "Could not wtite hash database settings."); + return false; + } + } + + /** + * Represents the serializable information within a hash lookup in order to + * be written to disk. Used to hand off information when loading and saving + * hash lookups. + */ + static final class HashDbInfo implements Serializable { + + private static final long serialVersionUID = 1L; + private final String hashSetName; + private final HashDbManager.HashDb.KnownFilesType knownFilesType; + private final boolean searchDuringIngest; + private final boolean sendIngestMessages; + private final String path; + + /** + * Constructs a HashDbInfo object + * + * @param hashSetName The name of the hash set + * @param knownFilesType The known files type + * @param searchDuringIngest Whether or not the db is searched during + * ingest + * @param sendIngestMessages Whether or not ingest messages are sent + * @param path The path to the db + */ + HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages, String path) { + this.hashSetName = hashSetName; + this.knownFilesType = knownFilesType; + this.searchDuringIngest = searchDuringIngest; + this.sendIngestMessages = sendIngestMessages; + this.path = path; + } + + /** + * Gets the hash set name. + * + * @return The hash set name. + */ + String getHashSetName() { + return hashSetName; + } + + /** + * Gets the known files type setting. + * + * @return The known files type setting. + */ + HashDbManager.HashDb.KnownFilesType getKnownFilesType() { + return knownFilesType; + } + + /** + * Gets the search during ingest setting. + * + * @return The search during ingest setting. + */ + boolean getSearchDuringIngest() { + return searchDuringIngest; + } + + /** + * Gets the send ingest messages setting. + * + * @return The send ingest messages setting. + */ + boolean getSendIngestMessages() { + return sendIngestMessages; + } + + /** + * Gets the path. + * + * @return The path. + */ + String getPath() { + return path; + } + } + + /** + * 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 HashLookupSettingsException extends Exception { + + private static final long serialVersionUID = 1L; + + HashLookupSettingsException(String message) { + super(message); + } + + HashLookupSettingsException(String message, Throwable throwable) { + super(message, throwable); + } + } +} diff --git a/NEWS.txt b/NEWS.txt index a4aece14bd..4335d9686a 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -3,7 +3,7 @@ Improvements: - VMWare virtual machine files (vmdk) and Microsoft Virtual Hard Drives (vhd) can be added as data sources. - New core ingest module detects vmdk and vhd files embedded in other data sources and adds them as data sources. - Text associated with artifacts posted to the blackboard is indexed and searched for keywords. -- Custom (user-defined) blackboard artifact and attribute types displayed in UI and included reports. +- Custom (user-defined) blackboard artifact and attribute types displayed in UI and included in reports. - File size and MIME type conditions can be specified for interesting files rules. - File size and MIME type conditions can be specified for file search by attributes. - Local/GMT time preference is used in reports.